blob: 76aa9c8109950e60a26cec1a9d30b2a1b06034be [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2020 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 glob
import json
import os
import shutil
import subprocess
import sys
"""
The sdk is organized as such:
//arch/
//arch/x64/...
//arch/{other_architectures}/...
//pkg/
//pkg/{libs}/
//pkg/sysroot/
//pkg/sysroot/meta.json
This script will create a new arch/ directory (--arch-name) by copying the
layout described in //pkg/sysroot/meta.json for an existing architecture.
This involves first copying headers which will be no different, and also
all shared objects in //pkg/arch/{existing_arch}/sysroot/lib. Most of these
shared objects are actually just linker scripts so they can be safely copied.
The other shared objects will be later replaced by //pkg/sysroot/*.ifs files,
currently this is libc.ifs and zircon.ifs. Scrt1.o is also replaced.
//pkg/arch/{new_arch}/lib is populated from shared objects created from
//pkg/*/*.ifs files (excluding //pkg/sysroot).
"""
TARGET_TO_TRIPLE = {
"x64": "x86_64-unknown-fuchsia",
"arm64": "aarch64-unknown-fuchsia",
"riscv64": "riscv64-unknown-fuchsia",
}
def get_filename_from_ifs(ifs_file):
ifs_file = os.path.basename(ifs_file)
# This is necessary because libc.ifs is not called c.ifs, which would be the
# conventional naming scheme for other libraries.
name = os.path.splitext(ifs_file)[0]
if not name.startswith("lib"):
name = "lib" + name
return name + ".so"
class SysrootGenerator:
def _copy_files(self, other_arch, other_paths, meta_json_key):
for src_path in other_paths:
path = src_path.replace(other_arch, self.arch_name, 1)
os.makedirs(
os.path.join(self.sdk_dir, os.path.dirname(path)), exist_ok=True
)
shutil.copyfile(
os.path.join(self.sdk_dir, src_path),
os.path.join(self.sdk_dir, path),
)
self.sysroot_meta["versions"][self.arch_name][meta_json_key].append(
path
)
def _copy_headers(self, other_arch, other_headers):
self._copy_files(other_arch, other_headers, "headers")
def _copy_link_libs(self, other_arch, other_link_libs):
self._copy_files(other_arch, other_link_libs, "link_libs")
self._make_Scrt1()
def _make_Scrt1(self):
# For bringing up new architectures executables won't be run anyway, this just creates
# an empty Scrt1.o.
Scrt1 = os.path.join(
self.sdk_dir, "arch", self.arch_name, "sysroot", "lib", "Scrt1.o"
)
open(Scrt1, "w")
def __init__(self, sdk_dir, arch_name):
self.sdk_dir = sdk_dir
self.arch_name = arch_name
def _create_from_dir_structure(self):
other_arch = os.listdir(os.path.join(self.sdk_dir, "arch"))[0]
def make_src_dst(dir):
return os.path.join(
self.sdk_dir, "arch", other_arch, "sysroot", dir
), os.path.join(
self.sdk_dir, "arch", self.arch_name, "sysroot", dir
)
src, dst = make_src_dst("include")
shutil.copytree(src, dst)
src, dst = make_src_dst("lib")
shutil.copytree(src, dst)
self._make_Scrt1()
def _create_from_meta_json(self, meta_json):
with open(meta_json) as f:
self.sysroot_meta = json.load(f)
other_arch, other_version = next(
iter(self.sysroot_meta["versions"].items())
)
other_headers, other_link_libs = (
other_version["headers"],
other_version["link_libs"],
)
self.sysroot_meta["versions"][self.arch_name] = {
"debug_libs": [],
"dist_dir": f"arch/{self.arch_name}/sysroot",
"dist_libs": [],
"headers": [],
"include_dir": f"arch/{self.arch_name}/sysroot/include",
"link_libs": [],
"root": f"arch/{self.arch_name}/sysroot",
}
self._copy_headers(other_arch, other_headers)
self._copy_link_libs(other_arch, other_link_libs)
def create(self):
meta_json = os.path.join(self.sdk_dir, "pkg", "sysroot", "meta.json")
if os.path.exists(meta_json):
self.emit_meta_json = True
self._create_from_meta_json(meta_json)
else:
pkg_sysroot = os.path.join(self.sdk_dir, "pkg", "sysroot")
self.emit_meta_json = False
self.sysroot_meta = {
"ifs_files": glob.glob("*.ifs", root_dir=pkg_sysroot)
}
self._create_from_dir_structure()
def add_stubs(self, create_stub):
for ifs_file in self.sysroot_meta["ifs_files"]:
source = os.path.join(self.sdk_dir, "pkg", "sysroot", ifs_file)
dest = os.path.join(
self.sdk_dir,
"arch",
self.arch_name,
"sysroot",
"lib",
get_filename_from_ifs(ifs_file),
)
create_stub(source, dest)
def finish(self):
if not self.emit_meta_json:
return
with open(
os.path.join(self.sdk_dir, "pkg", "sysroot", "meta.json"), "w"
) as f:
json.dump(self.sysroot_meta, f)
def create_libs(sdk_dir, arch_name, create_stub):
dirs = [
f
for f in os.listdir(os.path.join(sdk_dir, "pkg"))
if os.path.basename(f) != "sysroot"
]
for dir in dirs:
ifs_file = os.path.join(sdk_dir, "pkg", dir, f"{dir}.ifs")
if os.path.exists(ifs_file):
os.makedirs(
os.path.join(sdk_dir, "arch", arch_name, "lib"), exist_ok=True
)
create_stub(
ifs_file,
os.path.join(
sdk_dir,
"arch",
arch_name,
"lib",
get_filename_from_ifs(ifs_file),
),
)
def main():
parser = argparse.ArgumentParser()
parser.add_argument(
"--sdk-dir",
required=True,
help="Path to sdk, should have arch/ and pkg/ at top level",
)
parser.add_argument(
"--arch", required=True, help="ifs target to use for generating stubs"
)
parser.add_argument("--ifs-path", default="llvm-ifs")
args = parser.parse_args()
if os.path.exists(os.path.join(args.sdk_dir, "arch", args.arch)):
print(f"arch/{args.arch} already exists!")
return 1
def write_stub(src, dest):
subprocess.check_call(
[
args.ifs_path,
"--target",
TARGET_TO_TRIPLE[args.arch],
f"--output-elf={dest}",
src,
]
)
sysroot_creator = SysrootGenerator(args.sdk_dir, args.arch)
sysroot_creator.create()
sysroot_creator.add_stubs(write_stub)
sysroot_creator.finish()
# These are libs outside of the sysroot like fdio, etc.
create_libs(args.sdk_dir, args.arch, write_stub)
return 0
if __name__ == "__main__":
sys.exit(main())