blob: bc91b6ef63e913f381289bd934dec09a1b469fe8 [file] [log] [blame]
#!/usr/bin/env python3.8
# 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 os
import shutil
import stat
import sys
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
FUCHSIA_ROOT = os.path.dirname( # $root
os.path.dirname( # scripts
os.path.dirname( # sdk
SCRIPT_DIR))) # bazel
sys.path += [os.path.join(FUCHSIA_ROOT, 'scripts', 'sdk', 'common')]
from frontend import Frontend
from files import copy_tree
from create_test_workspace import create_test_workspace, SdkWorkspaceInfo
import template_model as model
def sanitize(name):
return name.replace('-', '_').replace('.', '_')
class BazelBuilder(Frontend):
def __init__(self, **kwargs):
self.install = kwargs.pop('install', True)
super(BazelBuilder, self).__init__(**kwargs)
self.has_dart = False
self.has_cc = False
self.dart_vendor_packages = {}
self.target_arches = []
self.workspace_info = SdkWorkspaceInfo()
def add_dart_vendor_package(self, name, version):
'''Adds a reference to a new Dart third-party package.'''
if name == 'flutter' and version == 'flutter_sdk':
# The Flutter SDK is set up separately.
return
if name in self.dart_vendor_packages:
existing_version = self.dart_vendor_packages[name]
if existing_version != version:
raise Exception(
'Dart package %s can only have one version; '
'%s and %s requested.' % (name, version, existing_version))
else:
self.dart_vendor_packages[name] = version
def prepare(self, arch, types):
self.target_arches = arch['target']
cc_types = ('sysroot', 'cc_source_library', 'cc_prebuilt_library')
self.has_cc = any(t in types for t in cc_types)
if self.install:
# Copy the common files.
shutil.copytree(self.local('base', 'common'), self.output)
# Copy C/C++ files if needed.
if self.has_cc:
copy_tree(self.local('base', 'cc'), self.output)
# Copy Dart files if needed.
if 'dart_library' in types:
# fxbug.dev/41514 : disable dart/flutter bits
# self.has_dart = True
# copy_tree(self.local('base', 'dart'), self.output)
pass
self.install_crosstool(arch)
self.workspace_info.with_cc = self.has_cc
self.workspace_info.with_dart = self.has_dart
self.workspace_info.target_arches = self.target_arches
def finalize(self, arch, types):
self.install_tools()
self.install_dart()
def install_tools(self):
'''Write BUILD files for tools directories.'''
if not self.install:
return
tools_root = os.path.join(self.output, 'tools')
for directory, _, _ in os.walk(tools_root, topdown=True):
self.write_file(os.path.join(directory, 'BUILD'), 'tools', {})
def install_crosstool(self, arches):
'''Generates crosstool.bzl based on the availability of sysroot
versions.
'''
if not self.has_cc or not self.install:
return
crosstool = model.Crosstool(arches['target'])
crosstool_base = self.dest('build_defs', 'internal', 'crosstool')
self.write_file(
self.dest(crosstool_base, 'crosstool.bzl'), 'crosstool_bzl',
crosstool)
self.write_file(
self.dest(crosstool_base, 'BUILD.crosstool'), 'crosstool',
crosstool)
self.write_file(
self.dest(crosstool_base, 'cc_toolchain_config.bzl.in'), 'cc_toolchain_config_bzl_in',
crosstool)
self.write_file(
self.dest('build_defs', 'toolchain', 'BUILD'), 'toolchain_build',
crosstool)
def install_dart(self):
if not self.has_dart or not self.install:
return
# Write the rule for setting up Dart packages.
# TODO(pylaligand): this process currently does not capture dependencies
# between vendor packages.
self.write_file(
self.dest('build_defs', 'setup_dart.bzl'), 'setup_dart_bzl',
self.dart_vendor_packages)
def install_dart_library_atom(self, atom):
if not self.has_dart or not self.install:
return
package_name = atom['name']
name = sanitize(package_name)
library = model.DartLibrary(name, package_name)
base = self.dest('dart', name)
self.copy_files(atom['sources'], atom['root'], base)
for dep in atom['deps']:
library.deps.append('//dart/' + sanitize(dep))
for dep in atom['fidl_deps']:
san_dep = sanitize(dep)
library.deps.append('//fidl/%s:%s_dart' % (san_dep, san_dep))
for dep in atom['third_party_deps']:
name = dep['name']
library.deps.append('@vendor_%s//:%s' % (name, name))
self.add_dart_vendor_package(name, dep['version'])
self.write_file(os.path.join(base, 'BUILD'), 'dart_library', library)
def install_cc_prebuilt_library_atom(self, atom):
name = sanitize(atom['name'])
include_paths = [os.path.relpath(h, atom['include_dir']) for h in atom['headers']]
self.workspace_info.headers['//pkg/' + name] = include_paths
if not self.install:
return
library = model.CppPrebuiltLibrary(name)
library.is_static = atom['format'] == 'static'
base = self.dest('pkg', name)
self.copy_files(atom['headers'], atom['root'], base, library.hdrs)
for arch in self.target_arches:
def _copy_prebuilt(path, category):
relative_dest = os.path.join(
'arch', arch, category, os.path.basename(path))
dest = self.dest(base, relative_dest)
shutil.copy2(self.source(path), dest)
return relative_dest
binaries = atom['binaries'][arch]
prebuilt_set = model.CppPrebuiltSet(
_copy_prebuilt(binaries['link'], 'lib'))
if 'dist' in binaries:
dist = binaries['dist']
prebuilt_set.dist_lib = _copy_prebuilt(dist, 'dist')
prebuilt_set.dist_path = 'lib/' + os.path.basename(dist)
if 'debug' in binaries:
self.copy_file(binaries['debug'])
library.prebuilts[arch] = prebuilt_set
for dep in atom['deps']:
library.deps.append('//pkg/' + sanitize(dep))
library.includes = os.path.relpath(atom['include_dir'], atom['root'])
self.write_file(
os.path.join(base, 'BUILD'), 'cc_prebuilt_library', library)
def install_cc_source_library_atom(self, atom):
name = sanitize(atom['name'])
include_paths = [os.path.relpath(h, atom['include_dir']) for h in atom['headers']]
self.workspace_info.headers['//pkg/' + name] = include_paths
if not self.install:
return
library = model.CppSourceLibrary(name)
base = self.dest('pkg', name)
self.copy_files(atom['headers'], atom['root'], base, library.hdrs)
self.copy_files(atom['sources'], atom['root'], base, library.srcs)
for dep in atom['deps']:
library.deps.append('//pkg/' + sanitize(dep))
for dep in atom['fidl_deps']:
dep_name = sanitize(dep)
library.deps.append('//fidl/%s:%s_cc' % (dep_name, dep_name))
library.includes = os.path.relpath(atom['include_dir'], atom['root'])
self.write_file(os.path.join(base, 'BUILD'), 'cc_library', library)
def install_sysroot_atom(self, atom):
if not self.install:
return
for arch in self.target_arches:
base = self.dest('arch', arch, 'sysroot')
arch_data = atom['versions'][arch]
self.copy_files(arch_data['headers'], arch_data['root'], base)
self.copy_files(arch_data['link_libs'], arch_data['root'], base)
# We maintain debug files in their original location.
self.copy_files(arch_data['debug_libs'])
dist_libs = []
self.copy_files(
arch_data['dist_libs'], arch_data['root'], base, dist_libs)
version = {}
for lib in dist_libs:
version['lib/' + os.path.basename(lib)] = lib
self.write_file(
os.path.join(base, 'BUILD'), 'sysroot_version', version)
self.write_file(
self.dest('pkg', 'sysroot', 'BUILD'), 'sysroot_pkg',
self.target_arches)
def install_host_tool_atom(self, atom):
if not self.install:
return
if 'files' in atom:
self.copy_files(atom['files'], atom['root'], 'tools')
if 'target_files' in atom:
for files in atom['target_files'].values():
self.copy_files(files, atom['root'], 'tools')
def install_fidl_library_atom(self, atom):
name = sanitize(atom['name'])
self.workspace_info.fidl_libraries.append(name)
if not self.install:
return
data = model.FidlLibrary(name, atom['name'])
data.with_cc = self.has_cc
data.with_dart = self.has_dart
base = self.dest('fidl', name)
self.copy_files(atom['sources'], atom['root'], base, data.srcs)
for dep in atom['deps']:
data.deps.append(sanitize(dep))
self.write_file(os.path.join(base, 'BUILD'), 'fidl', data)
def main():
parser = argparse.ArgumentParser(
description='Lays out a Bazel workspace for a given SDK tarball.')
source_group = parser.add_mutually_exclusive_group(required=True)
source_group.add_argument(
'--archive', help='Path to the SDK archive to ingest', default='')
source_group.add_argument(
'--directory', help='Path to the SDK directory to ingest', default='')
parser.add_argument(
'--output',
help='Path to the directory where to install the SDK',
required=True)
parser.add_argument(
'--tests', help='Path to the directory where to generate tests')
parser.add_argument(
'--nosdk',
action='store_false',
dest='install_sdk',
help='''Do not
install the Bazel SDK. If --tests is set, the tests will still be
generated, but the Bazel SDK must be installed at the location specified
by --output before running the tests.''')
args = parser.parse_args()
if args.install_sdk:
# Remove any existing output.
shutil.rmtree(args.output, ignore_errors=True)
builder = BazelBuilder(
archive=args.archive,
directory=args.directory,
output=args.output,
local_dir=SCRIPT_DIR,
install=args.install_sdk)
if not builder.run():
return 1
if args.tests and not create_test_workspace(args.output, args.tests,
builder.workspace_info):
return 1
return 0
if __name__ == '__main__':
sys.exit(main())