| #!/usr/bin/env python |
| # Copyright 2018 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import datetime |
| import json |
| import os |
| import re |
| import subprocess |
| import sys |
| |
| ROOT_PATH = os.path.abspath(__file__ + "/../../..") |
| TOML_DEFAULT_PATH = os.path.join(ROOT_PATH, "garnet", "rust_fidl_crates.json") |
| WORKSPACE_TOML_DEFAULT_PATH = os.path.join(ROOT_PATH, "garnet", "Cargo.toml") |
| OUT_PUB_DEFAULT_PATH = os.path.join(ROOT_PATH, "garnet", "public", "rust", "fidl_crates") |
| OUT_PRIV_DEFAULT_PATH = os.path.join(ROOT_PATH, "garnet", "lib", "rust", "fidl_crates") |
| |
| BUILD_GN_CONTENTS = '''\ |
| # Copyright %(year)s The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import("//build/rust/rust_library.gni") |
| import("//build/rust/toolchain.gni") |
| |
| rust_library("%(crate_name)s") { |
| deps = [ |
| "//%(fidl_target)s:%(fidl_label)s($rust_toolchain)", |
| ] |
| } |
| ''' |
| |
| CARGO_TOML_CONTENTS = '''\ |
| # Copyright %(year)s The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| [package] |
| name = "%(crate_name)s" |
| version = "0.1.0" |
| license = "BSD-3-Clause" |
| authors = ["rust-fuchsia@fuchsia.com"] |
| description = "Generated FIDL interface" |
| repository = "https://fuchsia.googlesource.com/garnet/" |
| |
| [dependencies] |
| fidl = "0.1" |
| fuchsia-zircon = "0.3" |
| futures = "=0.2.0-alpha" |
| fuchsia-async = "0.1" |
| %(deps)s |
| ''' |
| |
| LIB_RS_CONTENTS = '''\ |
| // Copyright %(year)s The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| include!(concat!(env!("FIDL_GEN_ROOT"), "/%(path)s")); |
| ''' |
| |
| def cur_year(): |
| return datetime.datetime.now().year |
| |
| def rm_left_slashes(path): |
| if path.startswith("//"): |
| return path[len("//"):] |
| else: |
| return path |
| |
| def crate_name_from_target(path): |
| return "_".join(filter(lambda x: len(x) > 0, re.split(":|\/", path))) |
| |
| def ensure_dir(path): |
| if not os.path.exists(path): |
| os.makedirs(path) |
| |
| def write_lib_rs(crate_path, label_name, fidl_target_fs_path): |
| ensure_dir(crate_path) |
| src_path = os.path.join(crate_path, "src") |
| ensure_dir(src_path) |
| lib_rs_path = os.path.join(src_path, "lib.rs") |
| path_to_generated_code = "%s/%s.fidl.rs" % (fidl_target_fs_path, label_name) |
| with open(lib_rs_path, "w") as f: |
| f.write(LIB_RS_CONTENTS % { "year": cur_year(), "path": path_to_generated_code }) |
| |
| def write_cargo_toml_file(crate_path, crate_name, deps): |
| cargo_toml_path = os.path.join(crate_path, "Cargo.toml") |
| cargo_deps = "\n".join(map(lambda x: '%s = "0.1"' % crate_name_from_target(x), deps)) |
| with open(cargo_toml_path, "w") as f: |
| f.write(CARGO_TOML_CONTENTS % { |
| "crate_name": crate_name, |
| "deps": cargo_deps, |
| "year": cur_year() |
| }) |
| |
| def write_build_gn_file(crate_path, crate_name, fidl_target_fs_path, fidl_target_label): |
| build_gn_path = os.path.join(crate_path, "BUILD.gn") |
| with open(build_gn_path, "w") as f: |
| f.write(BUILD_GN_CONTENTS % { |
| "crate_name": crate_name, |
| "fidl_target": fidl_target_fs_path, |
| "fidl_label": fidl_target_label, |
| "year": cur_year(), |
| }) |
| # Format the file |
| command_args = [ os.path.join(ROOT_PATH, "scripts", "fx"), "gn", "format", build_gn_path] |
| subprocess.check_call(command_args) |
| |
| def write_facade(folder_path, fidl_target, deps): |
| fidl_target_sections = fidl_target.split(":") |
| assert len(fidl_target_sections) == 2 |
| fidl_target_gn_path = fidl_target_sections[0] |
| fidl_target_label = fidl_target_sections[1] |
| fidl_target_fs_path = rm_left_slashes(fidl_target_gn_path) |
| crate_name = crate_name_from_target(fidl_target) |
| crate_path = os.path.join(folder_path, crate_name) |
| write_lib_rs(crate_path, fidl_target_label, fidl_target_fs_path) |
| write_cargo_toml_file(crate_path, crate_name, deps) |
| write_build_gn_file(crate_path, crate_name, fidl_target_fs_path, fidl_target_label) |
| return (crate_name, crate_path) |
| |
| def insert_alphabetically_between(lines, line_to_add, first_line_stripped, last_line_stripped): |
| # Find the start of the area to insert |
| i = 0 |
| while True: |
| if len(lines) == i: |
| assert False, "Start of alphabetical list not found" |
| if "".join(lines[i].split()) == first_line_stripped: |
| first_line = i + 1 |
| break |
| i += 1 |
| |
| line_to_add_stripped = "".join(line_to_add.split()) |
| |
| i = first_line |
| while i < len(lines): |
| stripped_line = "".join(lines[i].split()) |
| if stripped_line == line_to_add_stripped: |
| # If our line has already been added, we can return |
| return lines |
| |
| if stripped_line == last_line_stripped or stripped_line > line_to_add_stripped: |
| # We've found the first line that should be after our line alphabetically |
| # Insert the line at the position just before the last line read |
| lines.insert(i, line_to_add) |
| |
| # Remove any non-alphabetical copies of the entry we just added |
| i += 1 |
| while i < len(lines): |
| stripped_line = "".join(lines[i].split()) |
| if stripped_line == line_to_add_stripped: |
| lines.pop(i) |
| else: |
| i += 1 |
| return lines |
| |
| i += 1 |
| |
| if last_line_stripped != "": |
| assert False, "Reached end of file before end of alphabetical list found" |
| else: |
| lines.append(line_to_add) |
| return lines |
| |
| def update_workspace_cargo_toml(workspace_toml_path, crate_names_and_paths): |
| with open(workspace_toml_path, "r+") as f: |
| content = f.readlines() |
| for (crate_name, crate_path) in crate_names_and_paths: |
| crate_rel_path = os.path.relpath(crate_path, os.path.join(workspace_toml_path, os.pardir)) |
| content = insert_alphabetically_between( |
| content, ' "%s",\n' % crate_rel_path, "members=[", "]") |
| content = insert_alphabetically_between( |
| content, '%s = { path = "%s" }\n' % (crate_name, crate_rel_path), "[patch.crates-io]", "") |
| f.seek(0) |
| f.write("".join(content)) |
| f.truncate() |
| |
| def main(): |
| parser = argparse.ArgumentParser("Generate Rust FIDL facade crates") |
| parser.add_argument("--target-json", |
| help="Path to the JSON file listing the crates to generate", |
| default=TOML_DEFAULT_PATH) |
| parser.add_argument("--out-pub", |
| help="Path to the output directory for public crates", |
| default=OUT_PUB_DEFAULT_PATH) |
| parser.add_argument("--out-priv", |
| help="Path to the output directory for private crates", |
| default=OUT_PRIV_DEFAULT_PATH) |
| parser.add_argument("--workspace-toml", |
| help="Path to the workspace Cargo.toml file", |
| default=WORKSPACE_TOML_DEFAULT_PATH) |
| args = parser.parse_args() |
| |
| with open(args.target_json, "r") as file: |
| target_json = json.load(file) |
| |
| crate_names_and_paths = [] |
| |
| for crate in target_json["public"]: |
| crate_names_and_paths.append( |
| write_facade(args.out_pub, crate["fidl_target"], crate["deps"])) |
| |
| for crate in target_json["private"]: |
| crate_names_and_paths.append( |
| write_facade(args.out_priv, crate["fidl_target"], crate["deps"])) |
| |
| update_workspace_cargo_toml(args.workspace_toml, crate_names_and_paths) |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |
| |