blob: 65f9066d048dd21fdd54b616a75d4956a8b8960e [file] [log] [blame]
#!/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())