blob: 31d408284f56a84bad9945550a3f06cb4b584dd9 [file] [log] [blame]
# 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.
"""Recipe for building tools that use GNU build system."""
from collections import namedtuple
from recipe_engine.config import Enum, ReturnSchema, Single
from recipe_engine.recipe_api import Property
DEPS = [
"fuchsia/archive",
"fuchsia/macos_sdk",
"fuchsia/status_check",
"fuchsia/upload",
"recipe_engine/buildbucket",
"recipe_engine/cipd",
"recipe_engine/context",
"recipe_engine/file",
"recipe_engine/path",
"recipe_engine/platform",
"recipe_engine/raw_io",
"recipe_engine/step",
]
def sysroot(api, cipd_dir):
if api.platform.is_linux:
return cipd_dir.join("sysroot")
elif api.platform.is_mac: # pragma: no cover
# TODO(fxbug.dev/3043): Eventually use our own hermetic sysroot as for Linux.
step_result = api.step(
"xcrun",
["xcrun", "--sdk", "macosx", "--show-sdk-path"],
stdout=api.raw_io.output(name="sdk-path", add_output_log=True),
step_test_data=lambda: api.raw_io.test_api.stream_output(
"/some/xcode/path"
),
)
return step_result.stdout.strip()
raise api.step.StepFailure("unsupported platform") # pragma: no cover
def build(
api,
name,
version,
cipd_dir,
platform,
patches=(),
options=(),
cflags=(),
cxxflags=(),
ldflags=(),
):
work_dir = api.path["start_dir"].join(name)
with api.context(infra_steps=True):
src_dir = work_dir.join("source")
pkgs = api.cipd.EnsureFile()
pkgs.add_package("fuchsia/third_party/source/%s" % name, "version:%s" % version)
api.cipd.ensure(src_dir, pkgs)
build_dir = work_dir.join("build")
install_dir = work_dir.join("install")
api.file.ensure_directory("create build dir", build_dir)
with api.context(cwd=build_dir):
flags = ["--sysroot=%s" % sysroot(api, cipd_dir), "-O3"]
if not api.platform.is_mac:
# TODO: LTO is currently failing on macOS
flags.append("-flto")
variables = {
"CC": cipd_dir.join("bin", "clang"),
"CXX": cipd_dir.join("bin", "clang++"),
"CFLAGS": " ".join(flags + list(cflags)),
"CPPFLAGS": " ".join(flags + list(cflags)),
"CXXFLAGS": " ".join(flags + list(cxxflags)),
"LDFLAGS": " ".join(flags + list(ldflags)),
}
if api.platform.is_linux:
variables.update(
{
"AR": cipd_dir.join("bin", "llvm-ar"),
"RANLIB": cipd_dir.join("bin", "llvm-ranlib"),
"NM": cipd_dir.join("bin", "llvm-nm"),
"STRIP": cipd_dir.join("bin", "llvm-strip"),
"OBJCOPY": cipd_dir.join("bin", "llvm-objcopy"),
}
)
for patch in patches:
api.step(
"apply %s" % api.path.basename(patch),
["patch", "-d", src_dir, "-i", patch, "-p1"],
)
api.step(
"configure",
[src_dir.join("configure"), "--prefix=", "--disable-silent-rules"]
+ list(options)
+ sorted(["%s=%s" % (k, v) for k, v in variables.iteritems()]),
)
api.step(
"make", ["make", "-j%d" % api.platform.cpu_count],
)
# TODO: run `make check`
api.step(
"make install", ["make", "install", "DESTDIR=%s" % install_dir],
)
if api.buildbucket.builder_id.bucket == "ci":
isolated = api.archive.upload(install_dir, output_property="isolated")
if api.buildbucket.builder_id.bucket == "prod":
api.upload.cipd_package(
"fuchsia/third_party/%s/%s" % (name, platform),
install_dir,
[api.upload.DirectoryPath(install_dir)],
{"version": version},
)
return install_dir
def RunSteps(api):
# TODO: factor this out into a host_build recipe module.
platform = "%s-%s" % (
api.platform.name.replace("win", "windows"),
{"intel": {32: "386", 64: "amd64",}, "arm": {32: "armv6", 64: "arm64",},}[
api.platform.arch
][api.platform.bits],
)
with api.step.nest("ensure packages"):
with api.context(infra_steps=True):
cipd_dir = api.path["start_dir"].join("cipd")
pkgs = api.cipd.EnsureFile()
pkgs.add_package("fuchsia/third_party/clang/${platform}", "integration")
if api.platform.is_linux:
# TODO: factor this out into a linux_sdk recipe
pkgs.add_package(
"fuchsia/third_party/sysroot/linux",
"git_revision:c912d089c3d46d8982fdef76a50514cca79b6132",
"sysroot",
)
api.cipd.ensure(cipd_dir, pkgs)
Package = namedtuple(
"Package",
["name", "version", "patches", "options", "cflags", "cxxflags", "ldflags"],
)
Package.__new__.__defaults__ = (None, None, [], [], [], [], [])
# NOTE: packages are ordered in the order of dependencies.
packages = [
Package(
name="make", version="4.3", options=["--disable-dependency-tracking",],
),
Package(
name="m4", version="1.4.18", options=["--disable-dependency-tracking",],
),
Package(
name="autoconf",
version="2.69",
patches=[api.resource("0001-Make-autoconf-relocatable.patch")],
),
Package(
name="automake",
version="1.16.2",
patches=[api.resource("0001-Make-automake-relocatable.patch")],
),
Package(name="help2man", version="1.47.16",),
Package(
name="libtool",
version="2.4.6",
patches=[api.resource("0001-Make-libtool-relocatable.patch")],
options=[
"--disable-dependency-tracking",
"--disable-shared",
"--enable-ltdl-install",
"--enable-static",
],
),
Package(
name="pkg-config",
version="0.29.2",
options=[
"--disable-debug",
"--disable-host-tool",
"--disable-shared",
"--enable-static",
"--with-internal-glib",
"--with-pc-path=",
"--with-system-include-path=",
"--with-system-library-path=",
],
ldflags=["-framework CoreFoundation", "-framework CoreServices"]
if api.platform.is_mac
else [],
),
Package(
name="bison",
version="3.7",
options=[
"--disable-dependency-tracking",
"--enable-relocatable",
"--disable-rpath",
"--disable-nls",
],
),
Package(
name="flex",
version="2.6.4",
options=[
"--disable-dependency-tracking",
"--disable-shared",
"--disable-bootstrap",
"--disable-rpath",
"--disable-nls",
],
),
]
flatten = lambda l: [item for sublist in l for item in sublist]
with api.macos_sdk():
# TODO: to avoid full dependency tracking for now, we order the package
# in the order in which they depend on each other, and we ensure that
# each package can depend on all packages built before.
pkg_dirs = []
for package in packages:
with api.step.nest(package.name), api.context(
# Every package has access to all packages built before it.
env={
"PKG_CONFIG_PATH": "",
"PKG_CONFIG_SYSROOT_DIR": cipd_dir.join("sysroot"),
"PKG_CONFIG_ALLOW_SYSTEM_CFLAGS": 1,
"PKG_CONFIG_ALLOW_SYSTEM_LIBS": 1,
"PKG_CONFIG_LIBDIR": ":".join(
flatten(
[
[
str(d.join("lib", "pkgconfig")),
str(d.join("share", "pkgconfig")),
]
for d in pkg_dirs
]
)
),
},
env_prefixes={"PATH": [d.join("bin") for d in pkg_dirs]},
):
install_dir = build(
api,
package.name,
package.version,
cipd_dir,
platform,
package.patches,
package.options,
package.cflags,
package.cxxflags,
package.ldflags,
)
pkg_dirs.append(install_dir)
def GenTests(api):
yield api.status_check.test("ci") + api.buildbucket.ci_build(
project="fuchsia", bucket="ci",
) + api.platform.name("linux")
yield api.status_check.test("prod") + api.buildbucket.ci_build(
project="fuchsia", bucket="prod",
) + api.platform.name("mac")