Import Upstream version 0.0~git20221212.5e19d2f

This commit is contained in:
zhouganqing 2023-02-13 14:53:20 +08:00
commit d816a8e1ea
827 changed files with 144249 additions and 0 deletions

2
.clang-format Normal file
View File

@ -0,0 +1,2 @@
BasedOnStyle: Chromium
Standard: c++17

3
.editorconfig Normal file
View File

@ -0,0 +1,3 @@
[*.py]
indent_style = space
indent_size = 2

66
.gitignore vendored Normal file
View File

@ -0,0 +1,66 @@
*.bak
*.gypcmd
*.mk
*.ncb
*.opensdf
*.orig
*.pdb
*.props
*.pyc
*.pyproj
*.rules
*.sdf
*.sln
*.sublime-project
*.sublime-workspace
*.suo
*.targets
*.user
*.vc.opendb
*.vcproj
*.vcxproj
*.vcxproj.filters
*.vpj
*.vpw
*.vpwhistu
*.vtg
*.xcodeproj
*.xcworkspace
*.VC.db
*_proto.xml
*_proto_cpp.xml
*~
!Android.mk
.*.sw?
.DS_Store
.cache
.cipd
.classpath
.cproject
.gdb_history
.gdbinit
.landmines
.metadata
.project
.pydevproject
.recipe_deps
.checkstyle
compile_commands.json
cscope.*
out/
GPATH
GRTAGS
GSYMS
GTAGS
Session.vim
tags
Thumbs.db
# Settings directories for eclipse
/.externalToolBuilders/
/.settings/
/.vs/
# Visual Studio Code
/.vscode/
/_out
# VSChromium configuration file
vs-chromium-project.txt

20
.mailmap Normal file
View File

@ -0,0 +1,20 @@
# This file allows mapping several author and committer email addresses and
# names to a single canonical one for `git shortlog`, `git log --author`,
# or `git check-mailmap`.
#
# For example, if you commit as `random.person@example.com` but sometimes use
# "Rañdom Person" and sometimes "Random Person" as name and you want the former
# to be your canonical name, add
#
# Rañdom Person <random.person@example.com>
#
# If you commit as both `random.person@example.com` and `ranp@example.com` and
# you want the former to be your canonical email address, add
#
# <random.person@example.com> <ranp@example.com>
#
# Combinations of both are possible too, see
# https://git-scm.com/docs/gitmailmap for format details.
Nico Weber <thakis@chromium.org>
Nico Weber <thakis@chromium.org> <thakis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>

2
.style.yapf Normal file
View File

@ -0,0 +1,2 @@
[style]
based_on_style = chromium

53
AUTHORS Normal file
View File

@ -0,0 +1,53 @@
# Names should be added to this file with this pattern:
#
# For individuals:
# Name <email address>
#
# For organizations:
# Organization <fnmatch pattern>
#
# See python fnmatch module documentation for more information.
Google Inc. <*@google.com>
HyperConnect Inc. <*@hpcnt.com>
IBM Inc. <*@*.ibm.com>
Loongson Technology Corporation Limited. <*@loongson.cn>
MIPS Technologies, Inc. <*@mips.com>
NVIDIA Corporation <*@nvidia.com>
Opera Software ASA <*@opera.com>
Red Hat Inc. <*@redhat.com>
The Chromium Authors <*@chromium.org>
Vewd Software AS <*@vewd.com>
Vivaldi Technologies AS <*@vivaldi.com>
Yandex LLC <*@yandex-team.ru>
Alexis Menard <alexis.menard@intel.com>
Alfredo Mazzinghi <mzz.lrd@gmail.com>
Andrew Boyarshin <andrew.boyarshin@gmail.com>
Anuj Kumar Sharma <anujk.sharma@samsung.com>
DanCraft99 <simputest@gmail.com>
Evangelos Foutras <evangelos@foutrelis.com>
Gergely Nagy <ngg@ngg.hu>
Ilia K <ki.stfu@gmail.com>
Ivan Naydonov <samogot@gmail.com>
Joe Armstrong <joearmstrong334@gmail.com>
Julien Brianceau <jbriance@cisco.com>
Kal Conley <kcconley@gmail.com>
Kamil Rytarowski <krytarowski@gmail.com>
Ma Aiguo <maaiguo@uniontech.com>
Martijn Croonen <martijn@martijnc.be>
Matej Knopp <matej.knopp@gmail.com>
Michael Gilbert <floppymaster@gmail.com>
Milko Leporis <milko.leporis@imgtec.com>
Mohan Reddy <mohan.reddy@samsung.com>
Raphael Kubo da Costa <raphael.kubo.da.costa@intel.com>
Riku Voipio <riku.voipio@linaro.org>
Saikrishna Arcot <saiarcot895@gmail.com>
Stephan Hartmann <stha09@googlemail.com>
Tim Niederhausen <tim@rnc-ag.de>
Tomas Popela <tomas.popela@gmail.com>
Tripta Gupta <tripta.g@samsung.com>
Wink Saville <wink@saville.com>
Yuriy Taraday <yorik.sar@gmail.com>
Oleksandr Motsok <boramaabak@gmail.com>
Ihor Karavan <ihorkaravan96@gmail.com>

27
LICENSE Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

4
OWNERS Normal file
View File

@ -0,0 +1,4 @@
brettw@chromium.org
phosek@chromium.org
scottmg@chromium.org
sdefresne@chromium.org

218
README.md Normal file
View File

@ -0,0 +1,218 @@
# GN
GN is a meta-build system that generates build files for
[Ninja](https://ninja-build.org).
Related resources:
* Documentation in [docs/](https://gn.googlesource.com/gn/+/main/docs/). In
particular:
* [GN quick start guide](https://gn.googlesource.com/gn/+/main/docs/quick_start.md).
* [Frequently asked questions](https://gn.googlesource.com/gn/+/main/docs/faq.md)
* [Reference](https://gn.googlesource.com/gn/+/main/docs/reference.md)
(all builtin help converted to a single file).
* An introductory [presentation](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing).
* The [mailing list](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev).
* The [bug database](https://bugs.chromium.org/p/gn/issues/list).
## What GN is for
GN is currently used as the build system for Chromium, Fuchsia, and related
projects. Some strengths of GN are:
* It is designed for large projects and large teams. It scales efficiently to
many thousands of build files and tens of thousands of source files.
* It has a readable, clean syntax. Once a build is set-up, it is generally
easy for people with no backround in GN to make basic edits to the build.
* It is designed for multi-platform projects. It can cleanly express many
complicated build variants across different platforms. A single build
invocation can target multiple platforms.
* It supports multiple parallel output directories, each with their own
configuration. This allows a developer to maintain builds targeting debug,
release, or different platforms in parallel without forced rebuilds when
switching.
* It has a focus on correctness. GN checks for the correct dependencies,
inputs, and outputs to the extent possible, and has a number of tools to
allow developers to ensure the build evolves as desired (for example, `gn
check`, `testonly`, `assert_no_deps`).
* It has comprehensive build-in help available from the command-line.
Although small projects successfully use GN, the focus on large projects has
some disadvanages:
* GN has the goal of being minimally expressive. Although it can be quite
flexible, a design goal is to direct members of a large team (who may not
have much knowledge about the build) down an easy-to-understand, well-lit
path. This isn't necessarily the correct trade-off for smaller projects.
* The minimal build configuration is relatively heavyweight. There are several
files required and the exact way all compilers and linkers are run must be
specified in the configuration (see "Examples" below). There is no default
compiler configuration.
* It is not easily composable. GN is designed to compile a single large
project with relatively uniform settings and rules. Projects like Chromium
do bring together multiple repositories from multiple teams, but the
projects must agree on some conventions in the build files to allow this to
work.
* GN is designed with the expectation that the developers building a project
want to compile an identical configuration. So while builds can integrate
with the user's environment like the CXX and CFLAGS variables if they want,
this is not the default and most project's builds do not do this. The result
is that many GN projects do not integrate well with other systems like
ebuild.
* There is no simple release scheme (see "Versioning and distribution" below).
Projects are expected to manage the version of GN they require. Getting an
appropriate GN binary can be a hurdle for new contributors to a project.
Since GN is relatively uncommon, it can be more difficult to find
information and examples.
GN can generate Ninja build files for C, C++, Rust, Objective C, and Swift
source on most popular platforms. Other languages can be compiled using the
general "action" rules which are executed by Python or another scripting
language (Google does this to compile Java and Go). But because this is not as
clean, generally GN is only used when the bulk of the build is in one of the
main built-in languages.
## Getting a binary
You can download the latest version of GN binary for
[Linux](https://chrome-infra-packages.appspot.com/dl/gn/gn/linux-amd64/+/latest),
[macOS](https://chrome-infra-packages.appspot.com/dl/gn/gn/mac-amd64/+/latest) and
[Windows](https://chrome-infra-packages.appspot.com/dl/gn/gn/windows-amd64/+/latest)
from Google's build infrastructure (see "Versioning and distribution" below for
how this is expected to work).
Alternatively, you can build GN from source with a C++17 compiler:
git clone https://gn.googlesource.com/gn
cd gn
python build/gen.py # --allow-warning if you want to build with warnings.
ninja -C out
# To run tests:
out/gn_unittests
On Windows, it is expected that `cl.exe`, `link.exe`, and `lib.exe` can be found
in `PATH`, so you'll want to run from a Visual Studio command prompt, or
similar.
On Linux, Mac and z/OS, the default compiler is `clang++`, a recent version is
expected to be found in `PATH`. This can be overridden by setting the `CC`, `CXX`,
and `AR` environment variables.
On z/OS, building GN requires [ZOSLIB](https://github.com/ibmruntimes/zoslib) to be
installed, as described at that URL. When building with `build/gen.py`, use the option
`--zoslib-dir` to specify the path to [ZOSLIB](https://github.com/ibmruntimes/zoslib):
cd gn
python build/gen.py --zoslib-dir /path/to/zoslib
By default, if you don't specify `--zoslib-dir`, `gn/build/gen.py` expects to find
`zoslib` directory under `gn/third_party/`.
## Examples
There is a simple example in [examples/simple_build](examples/simple_build)
directory that is a good place to get started with the minimal configuration.
To build and run the simple example with the default gcc compiler:
cd examples/simple_build
../../out/gn gen -C out
ninja -C out
./out/hello
For a maximal configuration see the Chromium setup:
* [.gn](https://cs.chromium.org/chromium/src/.gn)
* [BUILDCONFIG.gn](https://cs.chromium.org/chromium/src/build/config/BUILDCONFIG.gn)
* [Toolchain setup](https://cs.chromium.org/chromium/src/build/toolchain/)
* [Compiler setup](https://cs.chromium.org/chromium/src/build/config/compiler/BUILD.gn)
and the Fuchsia setup:
* [.gn](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/.gn)
* [BUILDCONFIG.gn](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/build/config/BUILDCONFIG.gn)
* [Toolchain setup](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/build/toolchain/)
* [Compiler setup](https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/build/config/BUILD.gn)
## Reporting bugs
If you find a bug, you can see if it is known or report it in the [bug
database](https://bugs.chromium.org/p/gn/issues/list).
## Sending patches
GN uses [Gerrit](https://www.gerritcodereview.com/) for code review hosted at
[gn-review.googlesource.com](https://gn-review.googlesource.com/). The short
version of how to patch is:
Register at https://gn-review.googlesource.com.
... edit code ...
ninja -C out && out/gn_unittests
Then, to upload a change for review:
git commit
git push origin HEAD:refs/for/main
The first time you do this you'll get an error from the server about a missing
change-ID. Follow the directions in the error message to install the change-ID
hook and run `git commit --amend` to apply the hook to the current commit.
When revising a change, use:
git commit --amend
git push origin HEAD:refs/for/main
which will add the new changes to the existing code review, rather than creating
a new one.
We ask that all contributors
[sign Google's Contributor License Agreement](https://cla.developers.google.com/)
(either individual or corporate as appropriate, select 'any other Google
project').
## Community
You may ask questions and follow along with GN's development on Chromium's
[gn-dev@](https://groups.google.com/a/chromium.org/forum/#!forum/gn-dev)
Google Group.
## Versioning and distribution
Most open-source projects are designed to use the developer's computer's current
toolchain such as compiler, linker, and build tool. But the large
centrally controlled projects that GN is designed for typically want a more
hermetic environment. They will ensure that developers are using a specific
compatible toolchain that is versioned with the code.
As a result, GN expects that the project choose the appropriate version of GN
that will work with each version of the project. There is no "current stable
version" of GN that is expected to work for all projects.
As a result, the GN developers do not maintain any packages in any of the
various packaging systems (Debian, RedHat, HomeBrew, etc.). Some of these
systems to have GN packages, but they are maintained by third parties and you
should use them at your own risk. Instead, we recommend you refer your checkout
tooling to download binaries for a specific hash from [Google's build
infrastructure](https://chrome-infra-packages.appspot.com/p/gn/gn) or compile
your own.
GN does not guarantee the backwards-compatibility of new versions and has no
branches or versioning scheme beyond the sequence of commits to the main git
branch (which is expected to be stable).
In practice, however, GN is very backwards-compatible. The core functionality
has been stable for many years and there is enough GN code at Google alone to
make non-backwards-compatible changes very difficult, even if they were
desirable.
There have been discussions about adding a versioning scheme with some
guarantees about backwards-compatibility, but nothing has yet been implemented.

View File

@ -0,0 +1,13 @@
rule cxx
command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
rule alink_thin
command = rm -f $out && $ar rcsT $out $in
description = AR $out
rule link
command = $ld $ldflags -o $out $in $libs $solibs
description = LINK $out

View File

@ -0,0 +1,13 @@
rule cxx
command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
rule alink_thin
command = rm -f $out && $ar rcsT $out $in
description = AR $out
rule link
command = $ld $ldflags -o $out -Wl,--start-group $in $libs -Wl,--end-group $solibs
description = LINK $out

View File

@ -0,0 +1,13 @@
rule cxx
command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
rule alink_thin
command = rm -f $out && $ar rcsT $out $in
description = AR $out
rule link
command = $ld $ldflags -o $out -Wl,--start-group $in $libs -Wl,--end-group $solibs
description = LINK $out

View File

@ -0,0 +1,13 @@
rule cxx
command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
rule alink_thin
command = rm -f $out && $ar rcs $out $in
description = AR $out
rule link
command = $ld $ldflags -o $out $in $solibs $libs
description = LINK $out

View File

@ -0,0 +1,13 @@
rule cxx
command = $cxx -MMD -MF $out.d $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
rule alink_thin
command = rm -f $out && $ar rcs $out $in
description = AR $out
rule link
command = $ld $ldflags -o $out -Wl,--start-group $in $libs -Wl,--end-group $solibs
description = LINK $out

View File

@ -0,0 +1,12 @@
rule cxx
command = $cxx /nologo /showIncludes /FC $includes $cflags /c $in /Fo$out
description = CXX $out
deps = msvc
rule alink_thin
command = $ar /nologo /ignore:4221 $libflags /OUT:$out $in
description = LIB $out
rule link
command = $ld /nologo $ldflags /OUT:$out /PDB:$out.pdb $in $solibs $libs
description = LINK $out

View File

@ -0,0 +1,13 @@
rule cxx
command = $cxx $includes $cflags -c $in -o $out
description = CXX $out
depfile = $out.d
deps = gcc
rule alink_thin
command = rm -f $out && $ar rcsT $out $in
description = AR $out
rule link
command = $ld $ldflags -o $out $in $libs $solibs
description = LINK $out

81
build/full_test.py Executable file
View File

@ -0,0 +1,81 @@
#!/usr/bin/env python3
# Copyright 2018 The Chromium 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 os
import shutil
import subprocess
import sys
import timeit
IS_WIN = sys.platform.startswith('win')
def RemoveDir(d):
if os.path.isdir(d):
shutil.rmtree(d)
def Trial(gn_path_to_use, save_out_dir=None):
bin_path = os.path.join('out', 'gntrial')
if not os.path.isdir(bin_path):
os.makedirs(bin_path)
gn_to_run = os.path.join(bin_path, 'gn' + ('.exe' if IS_WIN else ''))
shutil.copy2(gn_path_to_use, gn_to_run)
comp_dir = os.path.join('out', 'COMP')
subprocess.check_call([gn_to_run, 'gen', comp_dir, '-q', '--check'])
if save_out_dir:
RemoveDir(save_out_dir)
shutil.move(comp_dir, save_out_dir)
def main():
if len(sys.argv) < 3 or len(sys.argv) > 4:
print('Usage: full_test.py /chrome/tree/at/762a25542878 rel_gn_path [clean]')
return 1
if len(sys.argv) == 4:
RemoveDir('out')
subprocess.check_call([sys.executable, os.path.join('build', 'gen.py')])
subprocess.check_call(['ninja', '-C', 'out'])
subprocess.check_call([os.path.join('out', 'gn_unittests')])
orig_dir = os.getcwd()
in_chrome_tree_gn = sys.argv[2]
our_gn = os.path.join(orig_dir, 'out', 'gn' + ('.exe' if IS_WIN else ''))
os.chdir(sys.argv[1])
# Check in-tree vs. ours. Uses:
# - Chromium tree at 762a25542878 in argv[1] (this can be off by a bit, but
# is roughly when GN was moved out of the Chrome tree, so matches in case GN
# semantics/ordering change after that.)
# - relative path to argv[1] built gn binary in argv[2]
# First, do a comparison to make sure the output between the two gn binaries
# actually matches.
print('Confirming output matches...')
dir_a = os.path.join('out', 'a')
dir_b = os.path.join('out', 'b')
Trial(in_chrome_tree_gn, dir_a)
Trial(our_gn, dir_b)
subprocess.check_call(['diff', '-r', dir_a, dir_b])
# Then, some time trials.
TRIALS = 5
print('Comparing performance... (takes a while)')
time_a = timeit.timeit('Trial("%s")' % in_chrome_tree_gn, number=TRIALS,
setup='from __main__ import Trial')
time_b = timeit.timeit('Trial("%s")' % our_gn, number=TRIALS,
setup='from __main__ import Trial')
print('In-tree gn avg: %.3fs' % (time_a / TRIALS))
print('Our gn avg: %.3fs' % (time_b / TRIALS))
return 0
if __name__ == '__main__':
sys.exit(main())

905
build/gen.py Executable file
View File

@ -0,0 +1,905 @@
#!/usr/bin/env python3
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates build.ninja that will build GN."""
import argparse
import os
import platform
import re
import shlex
import subprocess
import sys
# IMPORTANT: This script is also executed as python2 on
# GN's CI builders.
try: # py3
from shlex import quote as shell_quote
except ImportError: # py2
from pipes import quote as shell_quote
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
REPO_ROOT = os.path.dirname(SCRIPT_DIR)
class Platform(object):
"""Represents a host/target platform."""
def __init__(self, platform):
self._platform = platform
if self._platform is not None:
return
self._platform = sys.platform
if self._platform.startswith('linux'):
self._platform = 'linux'
elif self._platform.startswith('darwin'):
self._platform = 'darwin'
elif self._platform.startswith('mingw'):
self._platform = 'mingw'
elif self._platform.startswith('msys'):
self._platform = 'msys'
elif self._platform.startswith('win'):
self._platform = 'msvc'
elif self._platform.startswith('aix'):
self._platform = 'aix'
elif self._platform.startswith('fuchsia'):
self._platform = 'fuchsia'
elif self._platform.startswith('freebsd'):
self._platform = 'freebsd'
elif self._platform.startswith('netbsd'):
self._platform = 'netbsd'
elif self._platform.startswith('openbsd'):
self._platform = 'openbsd'
elif self._platform.startswith('haiku'):
self._platform = 'haiku'
elif self._platform.startswith('sunos'):
self._platform = 'solaris'
elif self._platform.startswith('zos'):
self._platform = 'zos'
@staticmethod
def known_platforms():
return ['linux', 'darwin', 'mingw', 'msys', 'msvc', 'aix', 'fuchsia', 'freebsd', 'netbsd', 'openbsd', 'haiku', 'solaris', 'zos']
def platform(self):
return self._platform
def is_linux(self):
return self._platform == 'linux'
def is_mingw(self):
return self._platform == 'mingw'
def is_msys(self):
return self._platform == 'msys'
def is_msvc(self):
return self._platform == 'msvc'
def is_windows(self):
return self.is_mingw() or self.is_msvc()
def is_darwin(self):
return self._platform == 'darwin'
def is_aix(self):
return self._platform == 'aix'
def is_haiku(self):
return self._platform == 'haiku'
def is_solaris(self):
return self._platform == 'solaris'
def is_posix(self):
return self._platform in ['linux', 'freebsd', 'darwin', 'aix', 'openbsd', 'haiku', 'solaris', 'msys', 'netbsd']
def is_zos(self):
return self._platform == 'zos'
class ArgumentsList:
"""Helper class to accumulate ArgumentParser argument definitions
and be able to regenerate a corresponding command-line to be
written in the generated Ninja file for the 'regen' rule.
"""
def __init__(self):
self._arguments = []
def add(self, *args, **kwargs):
"""Add an argument definition, use as argparse.ArgumentParser.add_argument()."""
self._arguments.append((args, kwargs))
def add_to_parser(self, parser):
"""Add all known arguments to parser."""
for args, kwargs in self._arguments:
parser.add_argument(*args, **kwargs)
def gen_command_line_args(self, parser_arguments):
"""Generate a gen.py argument list to be embedded in a Ninja file."""
result = []
for args, kwargs in self._arguments:
if len(args) == 2:
long_option = args[1]
else:
long_option = args[0]
dest = kwargs.get('dest', None)
if dest is None:
assert long_option.startswith('--')
dest = long_option[2:].replace('-', '_')
if getattr(parser_arguments, dest, None) is None:
# This was not set on the command-line so skip it.
continue
action = kwargs.get('action', None)
if action == 'store_true':
if getattr(parser_arguments, dest):
result.append(long_option)
elif action == 'store' or action is None:
result.append('%s=%s' % (long_option, getattr(parser_arguments, dest)))
elif action == 'append':
for item in getattr(parser_arguments, dest):
result.append('%s=%s' % (long_option, item))
else:
assert action is None, "Unsupported action " + action
return ' '.join(shell_quote(item) for item in result)
def main(argv):
parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__)
args_list = ArgumentsList()
args_list.add('-d', '--debug', action='store_true',
help='Do a debug build. Defaults to release build.')
args_list.add('--platform',
help='target platform (' +
'/'.join(Platform.known_platforms()) + ')',
choices=Platform.known_platforms())
args_list.add('--host',
help='host platform (' +
'/'.join(Platform.known_platforms()) + ')',
choices=Platform.known_platforms())
args_list.add('--use-lto', action='store_true',
help='Enable the use of LTO')
args_list.add('--use-icf', action='store_true',
help='Enable the use of Identical Code Folding')
args_list.add('--no-last-commit-position', action='store_true',
help='Do not generate last_commit_position.h.')
args_list.add('--out-path',
help='The path to generate the build files in.')
args_list.add('--no-strip', action='store_true',
help='Don\'t strip release build. Useful for profiling.')
args_list.add('--no-static-libstdc++', action='store_true',
default=False, dest='no_static_libstdcpp',
help='Don\'t link libstdc++ statically')
args_list.add('--link-lib',
action='append',
metavar='LINK_LIB',
default=[],
dest='link_libs',
help=('Add a library to the final executable link. ' +
'LINK_LIB must be the path to a static or shared ' +
'library, or \'-l<name>\' on POSIX systems. Can be ' +
'used multiple times. Useful to link custom malloc ' +
'or cpu profiling libraries.'))
args_list.add('--allow-warnings', action='store_true', default=False,
help=('Allow compiler warnings, don\'t treat them as '
'errors.'))
if sys.platform == 'zos':
args_list.add('--zoslib-dir',
action='store',
default='../third_party/zoslib',
dest='zoslib_dir',
help=('Specify the path of ZOSLIB directory, to link ' +
'with <ZOSLIB_DIR>/install/lib/libzoslib.a, and ' +
'add -I<ZOSLIB_DIR>/install/include to the compile ' +
'commands. See README.md for details.'))
args_list.add_to_parser(parser)
options = parser.parse_args(argv)
platform = Platform(options.platform)
if options.host:
host = Platform(options.host)
else:
host = platform
out_dir = options.out_path or os.path.join(REPO_ROOT, 'out')
if not os.path.isdir(out_dir):
os.makedirs(out_dir)
if not options.no_last_commit_position:
GenerateLastCommitPosition(host,
os.path.join(out_dir, 'last_commit_position.h'))
WriteGNNinja(os.path.join(out_dir, 'build.ninja'), platform, host, options, args_list)
return 0
def GenerateLastCommitPosition(host, header):
ROOT_TAG = 'initial-commit'
describe_output = subprocess.check_output(
['git', 'describe', 'HEAD', '--abbrev=12', '--match', ROOT_TAG],
shell=host.is_windows(), cwd=REPO_ROOT)
mo = re.match(ROOT_TAG + '-(\d+)-g([0-9a-f]+)', describe_output.decode())
if not mo:
raise ValueError(
'Unexpected output from git describe when generating version header')
contents = '''// Generated by build/gen.py.
#ifndef OUT_LAST_COMMIT_POSITION_H_
#define OUT_LAST_COMMIT_POSITION_H_
#define LAST_COMMIT_POSITION_NUM %s
#define LAST_COMMIT_POSITION "%s (%s)"
#endif // OUT_LAST_COMMIT_POSITION_H_
''' % (mo.group(1), mo.group(1), mo.group(2))
# Only write/touch this file if the commit position has changed.
old_contents = ''
if os.path.isfile(header):
with open(header, 'r') as f:
old_contents = f.read()
if old_contents != contents:
with open(header, 'w') as f:
f.write(contents)
def WriteGenericNinja(path, static_libraries, executables,
cxx, ar, ld, platform, host, options,
args_list, cflags=[], ldflags=[],
libflags=[], include_dirs=[], solibs=[]):
args = args_list.gen_command_line_args(options)
if args:
args = " " + args
ninja_header_lines = [
'cxx = ' + cxx,
'ar = ' + ar,
'ld = ' + ld,
'',
'rule regen',
' command = %s ../build/gen.py%s' % (sys.executable, args),
' description = Regenerating ninja files',
'',
'build build.ninja: regen',
' generator = 1',
' depfile = build.ninja.d',
'',
]
template_filename = os.path.join(SCRIPT_DIR, {
'msvc': 'build_win.ninja.template',
'mingw': 'build_linux.ninja.template',
'msys': 'build_linux.ninja.template',
'darwin': 'build_mac.ninja.template',
'linux': 'build_linux.ninja.template',
'freebsd': 'build_linux.ninja.template',
'aix': 'build_aix.ninja.template',
'openbsd': 'build_openbsd.ninja.template',
'haiku': 'build_haiku.ninja.template',
'solaris': 'build_linux.ninja.template',
'netbsd': 'build_linux.ninja.template',
'zos': 'build_zos.ninja.template',
}[platform.platform()])
with open(template_filename) as f:
ninja_template = f.read()
if platform.is_windows():
executable_ext = '.exe'
library_ext = '.lib'
object_ext = '.obj'
else:
executable_ext = ''
library_ext = '.a'
object_ext = '.o'
def escape_path_ninja(path):
return path.replace('$ ', '$$ ').replace(' ', '$ ').replace(':', '$:')
def src_to_obj(path):
return escape_path_ninja('%s' % os.path.splitext(path)[0] + object_ext)
def library_to_a(library):
return '%s%s' % (library, library_ext)
ninja_lines = []
def build_source(src_file, settings):
ninja_lines.extend([
'build %s: cxx %s' % (src_to_obj(src_file),
escape_path_ninja(
os.path.relpath(
os.path.join(REPO_ROOT, src_file),
os.path.dirname(path)))),
' includes = %s' % ' '.join(
['-I' + escape_path_ninja(dirname) for dirname in include_dirs]),
' cflags = %s' % ' '.join(cflags),
])
for library, settings in static_libraries.items():
for src_file in settings['sources']:
build_source(src_file, settings)
ninja_lines.append('build %s: alink_thin %s' % (
library_to_a(library),
' '.join([src_to_obj(src_file) for src_file in settings['sources']])))
ninja_lines.append(' libflags = %s' % ' '.join(libflags))
for executable, settings in executables.items():
for src_file in settings['sources']:
build_source(src_file, settings)
ninja_lines.extend([
'build %s%s: link %s | %s' % (
executable, executable_ext,
' '.join([src_to_obj(src_file) for src_file in settings['sources']]),
' '.join([library_to_a(library) for library in settings['libs']])),
' ldflags = %s' % ' '.join(ldflags),
' solibs = %s' % ' '.join(solibs),
' libs = %s' % ' '.join(
[library_to_a(library) for library in settings['libs']]),
])
ninja_lines.append('') # Make sure the file ends with a newline.
with open(path, 'w') as f:
f.write('\n'.join(ninja_header_lines))
f.write(ninja_template)
f.write('\n'.join(ninja_lines))
with open(path + '.d', 'w') as f:
f.write('build.ninja: ' +
os.path.relpath(os.path.join(SCRIPT_DIR, 'gen.py'),
os.path.dirname(path)) + ' ' +
os.path.relpath(template_filename, os.path.dirname(path)) + '\n')
def WriteGNNinja(path, platform, host, options, args_list):
if platform.is_msvc():
cxx = os.environ.get('CXX', 'cl.exe')
ld = os.environ.get('LD', 'link.exe')
ar = os.environ.get('AR', 'lib.exe')
elif platform.is_aix():
cxx = os.environ.get('CXX', 'g++')
ld = os.environ.get('LD', 'g++')
ar = os.environ.get('AR', 'ar -X64')
elif platform.is_msys() or platform.is_mingw():
cxx = os.environ.get('CXX', 'g++')
ld = os.environ.get('LD', 'g++')
ar = os.environ.get('AR', 'ar')
else:
cxx = os.environ.get('CXX', 'clang++')
ld = cxx
ar = os.environ.get('AR', 'ar')
cflags = os.environ.get('CFLAGS', '').split()
cflags += os.environ.get('CXXFLAGS', '').split()
ldflags = os.environ.get('LDFLAGS', '').split()
libflags = os.environ.get('LIBFLAGS', '').split()
include_dirs = [
os.path.relpath(os.path.join(REPO_ROOT, 'src'), os.path.dirname(path)),
'.',
]
if platform.is_zos():
include_dirs += [ options.zoslib_dir + '/install/include' ]
libs = []
if not platform.is_msvc():
if options.debug:
cflags.extend(['-O0', '-g'])
else:
cflags.append('-DNDEBUG')
cflags.append('-O3')
if options.no_strip:
cflags.append('-g')
ldflags.append('-O3')
# Use -fdata-sections and -ffunction-sections to place each function
# or data item into its own section so --gc-sections can eliminate any
# unused functions and data items.
cflags.extend(['-fdata-sections', '-ffunction-sections'])
ldflags.extend(['-fdata-sections', '-ffunction-sections'])
if platform.is_darwin():
ldflags.append('-Wl,-dead_strip')
elif not platform.is_aix() and not platform.is_solaris() and not platform.is_zos():
# Garbage collection is done by default on aix, and option is unsupported on z/OS.
ldflags.append('-Wl,--gc-sections')
# Omit all symbol information from the output file.
if options.no_strip is None:
if platform.is_darwin():
ldflags.append('-Wl,-S')
elif platform.is_aix():
ldflags.append('-Wl,-s')
elif platform.is_solaris():
ldflags.append('-Wl,--strip-all')
elif not platform.is_zos():
# /bin/ld on z/OS doesn't have an equivalent option.
ldflags.append('-Wl,-strip-all')
# Enable identical code-folding.
if options.use_icf and not platform.is_darwin():
ldflags.append('-Wl,--icf=all')
if options.use_lto:
cflags.extend(['-flto', '-fwhole-program-vtables'])
ldflags.extend(['-flto', '-fwhole-program-vtables'])
if not options.allow_warnings:
cflags.append('-Werror')
cflags.extend([
'-D_FILE_OFFSET_BITS=64',
'-D__STDC_CONSTANT_MACROS', '-D__STDC_FORMAT_MACROS',
'-pthread',
'-pipe',
'-fno-exceptions',
'-fno-rtti',
'-fdiagnostics-color',
'-Wall',
'-Wextra',
'-Wno-unused-parameter',
'-Wextra-semi',
'-Wundef',
'-std=c++17'
])
# flags not supported by gcc/g++.
if cxx == 'clang++':
cflags.extend(['-Wrange-loop-analysis', '-Wextra-semi-stmt'])
if platform.is_linux() or platform.is_mingw() or platform.is_msys():
ldflags.append('-Wl,--as-needed')
if not options.no_static_libstdcpp:
ldflags.append('-static-libstdc++')
if platform.is_mingw() or platform.is_msys():
cflags.remove('-std=c++17')
cflags.extend([
'-Wno-deprecated-copy',
'-Wno-implicit-fallthrough',
'-Wno-redundant-move',
'-Wno-unused-variable',
'-Wno-format', # Use of %llx, which is supported by _UCRT, false positive
'-Wno-strict-aliasing', # Dereferencing punned pointer
'-Wno-cast-function-type', # Casting FARPROC to RegDeleteKeyExPtr
'-std=gnu++17',
])
else:
# This is needed by libc++.
libs.append('-ldl')
elif platform.is_darwin():
min_mac_version_flag = '-mmacosx-version-min=10.9'
cflags.append(min_mac_version_flag)
ldflags.append(min_mac_version_flag)
elif platform.is_aix():
cflags.append('-maix64')
ldflags.append('-maix64')
elif platform.is_haiku():
cflags.append('-fPIC')
cflags.extend(['-D_BSD_SOURCE'])
elif platform.is_zos():
cflags.append('-fzos-le-char-mode=ascii')
cflags.append('-Wno-unused-function')
cflags.append('-D_OPEN_SYS_FILE_EXT')
cflags.append('-DPATH_MAX=1024')
if platform.is_posix() and not platform.is_haiku():
ldflags.append('-pthread')
if platform.is_mingw() or platform.is_msys():
cflags.extend(['-DUNICODE',
'-DNOMINMAX',
'-DWIN32_LEAN_AND_MEAN',
'-DWINVER=0x0A00',
'-D_CRT_SECURE_NO_DEPRECATE',
'-D_SCL_SECURE_NO_DEPRECATE',
'-D_UNICODE',
'-D_WIN32_WINNT=0x0A00',
'-D_HAS_EXCEPTIONS=0'
])
elif platform.is_msvc():
if not options.debug:
cflags.extend(['/O2', '/DNDEBUG', '/Zc:inline'])
ldflags.extend(['/OPT:REF'])
if options.use_icf:
libflags.extend(['/OPT:ICF'])
if options.use_lto:
cflags.extend(['/GL'])
libflags.extend(['/LTCG'])
ldflags.extend(['/LTCG'])
if not options.allow_warnings:
cflags.append('/WX')
cflags.extend([
'/DNOMINMAX',
'/DUNICODE',
'/DWIN32_LEAN_AND_MEAN',
'/DWINVER=0x0A00',
'/D_CRT_SECURE_NO_DEPRECATE',
'/D_SCL_SECURE_NO_DEPRECATE',
'/D_UNICODE',
'/D_WIN32_WINNT=0x0A00',
'/FS',
'/W4',
'/Zi',
'/wd4099',
'/wd4100',
'/wd4127',
'/wd4244',
'/wd4267',
'/wd4505',
'/wd4838',
'/wd4996',
'/std:c++17',
'/GR-',
'/D_HAS_EXCEPTIONS=0',
])
ldflags.extend(['/DEBUG', '/MACHINE:x64'])
static_libraries = {
'base': {'sources': [
'src/base/command_line.cc',
'src/base/environment.cc',
'src/base/files/file.cc',
'src/base/files/file_enumerator.cc',
'src/base/files/file_path.cc',
'src/base/files/file_path_constants.cc',
'src/base/files/file_util.cc',
'src/base/files/scoped_file.cc',
'src/base/files/scoped_temp_dir.cc',
'src/base/json/json_parser.cc',
'src/base/json/json_reader.cc',
'src/base/json/json_writer.cc',
'src/base/json/string_escape.cc',
'src/base/logging.cc',
'src/base/md5.cc',
'src/base/memory/ref_counted.cc',
'src/base/memory/weak_ptr.cc',
'src/base/sha1.cc',
'src/base/strings/string_number_conversions.cc',
'src/base/strings/string_split.cc',
'src/base/strings/string_util.cc',
'src/base/strings/string_util_constants.cc',
'src/base/strings/stringprintf.cc',
'src/base/strings/utf_string_conversion_utils.cc',
'src/base/strings/utf_string_conversions.cc',
'src/base/timer/elapsed_timer.cc',
'src/base/value_iterators.cc',
'src/base/values.cc',
]},
'gn_lib': {'sources': [
'src/gn/action_target_generator.cc',
'src/gn/action_values.cc',
'src/gn/analyzer.cc',
'src/gn/args.cc',
'src/gn/binary_target_generator.cc',
'src/gn/build_settings.cc',
'src/gn/builder.cc',
'src/gn/builder_record.cc',
'src/gn/bundle_data.cc',
'src/gn/bundle_data_target_generator.cc',
'src/gn/bundle_file_rule.cc',
'src/gn/builtin_tool.cc',
'src/gn/c_include_iterator.cc',
'src/gn/c_substitution_type.cc',
'src/gn/c_tool.cc',
'src/gn/command_analyze.cc',
'src/gn/command_args.cc',
'src/gn/command_check.cc',
'src/gn/command_clean.cc',
'src/gn/command_clean_stale.cc',
'src/gn/command_desc.cc',
'src/gn/command_format.cc',
'src/gn/command_gen.cc',
'src/gn/command_help.cc',
'src/gn/command_ls.cc',
'src/gn/command_meta.cc',
'src/gn/command_outputs.cc',
'src/gn/command_path.cc',
'src/gn/command_refs.cc',
'src/gn/commands.cc',
'src/gn/compile_commands_writer.cc',
'src/gn/rust_project_writer.cc',
'src/gn/config.cc',
'src/gn/config_values.cc',
'src/gn/config_values_extractors.cc',
'src/gn/config_values_generator.cc',
'src/gn/copy_target_generator.cc',
'src/gn/create_bundle_target_generator.cc',
'src/gn/deps_iterator.cc',
'src/gn/desc_builder.cc',
'src/gn/eclipse_writer.cc',
'src/gn/err.cc',
'src/gn/escape.cc',
'src/gn/exec_process.cc',
'src/gn/filesystem_utils.cc',
'src/gn/file_writer.cc',
'src/gn/frameworks_utils.cc',
'src/gn/function_exec_script.cc',
'src/gn/function_filter.cc',
'src/gn/function_foreach.cc',
'src/gn/function_forward_variables_from.cc',
'src/gn/function_get_label_info.cc',
'src/gn/function_get_path_info.cc',
'src/gn/function_get_target_outputs.cc',
'src/gn/function_process_file_template.cc',
'src/gn/function_read_file.cc',
'src/gn/function_rebase_path.cc',
'src/gn/function_set_default_toolchain.cc',
'src/gn/function_set_defaults.cc',
'src/gn/function_template.cc',
'src/gn/function_toolchain.cc',
'src/gn/function_write_file.cc',
'src/gn/functions.cc',
'src/gn/functions_target.cc',
'src/gn/general_tool.cc',
'src/gn/generated_file_target_generator.cc',
'src/gn/group_target_generator.cc',
'src/gn/header_checker.cc',
'src/gn/import_manager.cc',
'src/gn/inherited_libraries.cc',
'src/gn/input_conversion.cc',
'src/gn/input_file.cc',
'src/gn/input_file_manager.cc',
'src/gn/item.cc',
'src/gn/json_project_writer.cc',
'src/gn/label.cc',
'src/gn/label_pattern.cc',
'src/gn/lib_file.cc',
'src/gn/loader.cc',
'src/gn/location.cc',
'src/gn/metadata.cc',
'src/gn/metadata_walk.cc',
'src/gn/ninja_action_target_writer.cc',
'src/gn/ninja_binary_target_writer.cc',
'src/gn/ninja_build_writer.cc',
'src/gn/ninja_bundle_data_target_writer.cc',
'src/gn/ninja_c_binary_target_writer.cc',
'src/gn/ninja_copy_target_writer.cc',
'src/gn/ninja_create_bundle_target_writer.cc',
'src/gn/ninja_generated_file_target_writer.cc',
'src/gn/ninja_group_target_writer.cc',
'src/gn/ninja_rust_binary_target_writer.cc',
'src/gn/ninja_target_command_util.cc',
'src/gn/ninja_target_writer.cc',
'src/gn/ninja_toolchain_writer.cc',
'src/gn/ninja_tools.cc',
'src/gn/ninja_utils.cc',
'src/gn/ninja_writer.cc',
'src/gn/operators.cc',
'src/gn/output_conversion.cc',
'src/gn/output_file.cc',
'src/gn/parse_node_value_adapter.cc',
'src/gn/parse_tree.cc',
'src/gn/parser.cc',
'src/gn/path_output.cc',
'src/gn/pattern.cc',
'src/gn/pool.cc',
'src/gn/qt_creator_writer.cc',
'src/gn/runtime_deps.cc',
'src/gn/rust_substitution_type.cc',
'src/gn/rust_tool.cc',
'src/gn/rust_values.cc',
'src/gn/rust_values_generator.cc',
'src/gn/rust_variables.cc',
'src/gn/scheduler.cc',
'src/gn/scope.cc',
'src/gn/scope_per_file_provider.cc',
'src/gn/settings.cc',
'src/gn/setup.cc',
'src/gn/source_dir.cc',
'src/gn/source_file.cc',
'src/gn/standard_out.cc',
'src/gn/string_atom.cc',
'src/gn/string_output_buffer.cc',
'src/gn/string_utils.cc',
'src/gn/substitution_list.cc',
'src/gn/substitution_pattern.cc',
'src/gn/substitution_type.cc',
'src/gn/substitution_writer.cc',
'src/gn/swift_values.cc',
'src/gn/swift_values_generator.cc',
'src/gn/swift_variables.cc',
'src/gn/switches.cc',
'src/gn/target.cc',
'src/gn/target_generator.cc',
'src/gn/template.cc',
'src/gn/token.cc',
'src/gn/tokenizer.cc',
'src/gn/tool.cc',
'src/gn/toolchain.cc',
'src/gn/trace.cc',
'src/gn/value.cc',
'src/gn/value_extractors.cc',
'src/gn/variables.cc',
'src/gn/version.cc',
'src/gn/visibility.cc',
'src/gn/visual_studio_utils.cc',
'src/gn/visual_studio_writer.cc',
'src/gn/xcode_object.cc',
'src/gn/xcode_writer.cc',
'src/gn/xml_element_writer.cc',
'src/util/atomic_write.cc',
'src/util/exe_path.cc',
'src/util/msg_loop.cc',
'src/util/semaphore.cc',
'src/util/sys_info.cc',
'src/util/ticks.cc',
'src/util/worker_pool.cc',
]},
}
executables = {
'gn': {'sources': [ 'src/gn/gn_main.cc' ], 'libs': []},
'gn_unittests': { 'sources': [
'src/gn/action_target_generator_unittest.cc',
'src/gn/analyzer_unittest.cc',
'src/gn/args_unittest.cc',
'src/gn/builder_unittest.cc',
'src/gn/builder_record_map_unittest.cc',
'src/gn/c_include_iterator_unittest.cc',
'src/gn/command_format_unittest.cc',
'src/gn/commands_unittest.cc',
'src/gn/compile_commands_writer_unittest.cc',
'src/gn/config_unittest.cc',
'src/gn/config_values_extractors_unittest.cc',
'src/gn/escape_unittest.cc',
'src/gn/exec_process_unittest.cc',
'src/gn/filesystem_utils_unittest.cc',
'src/gn/file_writer_unittest.cc',
'src/gn/frameworks_utils_unittest.cc',
'src/gn/function_filter_unittest.cc',
'src/gn/function_foreach_unittest.cc',
'src/gn/function_forward_variables_from_unittest.cc',
'src/gn/function_get_label_info_unittest.cc',
'src/gn/function_get_path_info_unittest.cc',
'src/gn/function_get_target_outputs_unittest.cc',
'src/gn/function_process_file_template_unittest.cc',
'src/gn/function_rebase_path_unittest.cc',
'src/gn/function_template_unittest.cc',
'src/gn/function_toolchain_unittest.cc',
'src/gn/function_write_file_unittest.cc',
'src/gn/functions_target_rust_unittest.cc',
'src/gn/functions_target_unittest.cc',
'src/gn/functions_unittest.cc',
'src/gn/hash_table_base_unittest.cc',
'src/gn/header_checker_unittest.cc',
'src/gn/inherited_libraries_unittest.cc',
'src/gn/input_conversion_unittest.cc',
'src/gn/json_project_writer_unittest.cc',
'src/gn/rust_project_writer_unittest.cc',
'src/gn/rust_project_writer_helpers_unittest.cc',
'src/gn/label_pattern_unittest.cc',
'src/gn/label_unittest.cc',
'src/gn/loader_unittest.cc',
'src/gn/metadata_unittest.cc',
'src/gn/metadata_walk_unittest.cc',
'src/gn/ninja_action_target_writer_unittest.cc',
'src/gn/ninja_binary_target_writer_unittest.cc',
'src/gn/ninja_build_writer_unittest.cc',
'src/gn/ninja_bundle_data_target_writer_unittest.cc',
'src/gn/ninja_c_binary_target_writer_unittest.cc',
'src/gn/ninja_copy_target_writer_unittest.cc',
'src/gn/ninja_create_bundle_target_writer_unittest.cc',
'src/gn/ninja_generated_file_target_writer_unittest.cc',
'src/gn/ninja_group_target_writer_unittest.cc',
'src/gn/ninja_rust_binary_target_writer_unittest.cc',
'src/gn/ninja_target_command_util_unittest.cc',
'src/gn/ninja_target_writer_unittest.cc',
'src/gn/ninja_toolchain_writer_unittest.cc',
'src/gn/operators_unittest.cc',
'src/gn/output_conversion_unittest.cc',
'src/gn/parse_tree_unittest.cc',
'src/gn/parser_unittest.cc',
'src/gn/path_output_unittest.cc',
'src/gn/pattern_unittest.cc',
'src/gn/pointer_set_unittest.cc',
'src/gn/resolved_target_deps_unittest.cc',
'src/gn/runtime_deps_unittest.cc',
'src/gn/scope_per_file_provider_unittest.cc',
'src/gn/scope_unittest.cc',
'src/gn/setup_unittest.cc',
'src/gn/source_dir_unittest.cc',
'src/gn/source_file_unittest.cc',
'src/gn/string_atom_unittest.cc',
'src/gn/string_output_buffer_unittest.cc',
'src/gn/string_utils_unittest.cc',
'src/gn/substitution_pattern_unittest.cc',
'src/gn/substitution_writer_unittest.cc',
'src/gn/target_public_pair_unittest.cc',
'src/gn/target_unittest.cc',
'src/gn/template_unittest.cc',
'src/gn/test_with_scheduler.cc',
'src/gn/test_with_scope.cc',
'src/gn/tokenizer_unittest.cc',
'src/gn/unique_vector_unittest.cc',
'src/gn/value_unittest.cc',
'src/gn/vector_utils_unittest.cc',
'src/gn/version_unittest.cc',
'src/gn/visibility_unittest.cc',
'src/gn/visual_studio_utils_unittest.cc',
'src/gn/visual_studio_writer_unittest.cc',
'src/gn/xcode_object_unittest.cc',
'src/gn/xml_element_writer_unittest.cc',
'src/util/atomic_write_unittest.cc',
'src/util/test/gn_test.cc',
], 'libs': []},
}
if platform.is_posix() or platform.is_zos():
static_libraries['base']['sources'].extend([
'src/base/files/file_enumerator_posix.cc',
'src/base/files/file_posix.cc',
'src/base/files/file_util_posix.cc',
'src/base/posix/file_descriptor_shuffle.cc',
'src/base/posix/safe_strerror.cc',
])
if platform.is_zos():
libs.extend([ options.zoslib_dir + '/install/lib/libzoslib.a' ])
if platform.is_windows():
static_libraries['base']['sources'].extend([
'src/base/files/file_enumerator_win.cc',
'src/base/files/file_util_win.cc',
'src/base/files/file_win.cc',
'src/base/win/registry.cc',
'src/base/win/scoped_handle.cc',
'src/base/win/scoped_process_information.cc',
])
if platform.is_msvc():
libs.extend([
'advapi32.lib',
'dbghelp.lib',
'kernel32.lib',
'ole32.lib',
'shell32.lib',
'user32.lib',
'userenv.lib',
'version.lib',
'winmm.lib',
'ws2_32.lib',
'Shlwapi.lib',
])
else:
libs.extend([
'-ladvapi32',
'-ldbghelp',
'-lkernel32',
'-lole32',
'-lshell32',
'-luser32',
'-luserenv',
'-lversion',
'-lwinmm',
'-lws2_32',
'-lshlwapi',
])
libs.extend(options.link_libs)
# we just build static libraries that GN needs
executables['gn']['libs'].extend(static_libraries.keys())
executables['gn_unittests']['libs'].extend(static_libraries.keys())
WriteGenericNinja(path, static_libraries, executables, cxx, ar, ld,
platform, host, options, args_list,
cflags, ldflags, libflags, include_dirs, libs)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

126
docs/cross_compiles.md Normal file
View File

@ -0,0 +1,126 @@
# How GN handles cross-compiling
## As a GN user
GN has robust support for doing cross compiles and building things for
multiple architectures in a single build (e.g., to build some things to
run locally and some things to run on an embedded device). In fact,
there is no limit on the number of different architectures you can build
at once; the Chromium build uses at least four in some configurations.
To start, GN has the concepts of a _host_ and a _target_. The host is
the platform that the build is run on, and the target is the platform
where the code will actually run (This is different from
[autotools](http://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html)'
terminology, but uses the more common terminology for cross
compiling).
(Confusingly, GN also refers to each build artifact -- an executable,
library, etc. -- as a target. On this page, we will use "target" only to
refer to the system you want to run your code on, and use "rule" or some
other synonym to refer to a specific build artifact).
When GN starts up, the `host_os` and `host_cpu` variables are set
automatically to match the operating system (they can be overridden in
args files, which can be useful in weird corner cases). The user can
specify that they want to do a cross-compile by setting either or both
of `target_os` and `target_cpu`; if they are not set, the build config
files will usually set them to the host's values, though the Chromium
build will set target\_cpu to "arm" if target\_os is set to "android").
So, for example, running on an x64 Linux machine:
```
gn gen out/Default
```
is equivalent to:
```
gn gen out/Default --args='target_os="linux" target_cpu="x64"'
```
To do an 32-bit ARM Android cross-compile, do:
```
gn gen out/Default --args='target_os="android"'
```
(We don't have to specify target\_cpu because of the conditionals
mentioned above).
And, to do a 64-bit MIPS Chrome OS cross-compile:
```
gn gen out/Default --args='target_os="chromeos" target_cpu="mips64el"'
```
## As a BUILD.gn author
If you are editing build files outside of the //build directory (i.e.,
not directly working on toolchains, compiler configs, etc.), generally
you only need to worry about a few things:
The `current_toolchain`, `current_cpu`, and `current_os` variables
reflect the settings that are **currently** in effect in a given rule.
The `is_linux`, `is_win` etc. variables are updated to reflect the
current settings, and changes to `cflags`, `ldflags` and so forth also
only apply to the current toolchain and the current thing being built.
You can also refer to the `target_cpu` and `target_os` variables. This
is useful if you need to do something different on the host depending on
which target\_arch is requested; the values are constant across all
toolchains. You can do similar things for the `host_cpu` and `host_os`
variables, but should generally never need to.
For the default toolchain, `target_cpu` and `current_cpu` are the same. For a
secondary toolchain, `current_cpu` is set based on the toolchain definition
and `target_cpu` remains the same. When writing rules, **`current_cpu` should
be used rather than `target_cpu` most of the time**.
By default, dependencies listed in the `deps` variable of a rule use the
same (currently active) toolchain. You may specify a different toolchain
using the `foo(bar)` label notation as described in [the label section
of the reference doc](reference.md#Toolchains).
Here's an example of when to use `target_cpu` vs `current_cpu`:
```
declare_args() {
# Applies only to toolchains targeting target_cpu.
sysroot = ""
}
config("my_config") {
# Uses current_cpu because compile flags are toolchain-dependent.
if (current_cpu == "arm") {
defines = [ "CPU_IS_32_BIT" ]
} else {
defines = [ "CPU_IS_64_BIT" ]
}
# Compares current_cpu with target_cpu to see whether current_toolchain
# has the same architecture as target_toolchain.
if (sysroot != "" && current_cpu == target_cpu) {
cflags = [
"-isysroot",
sysroot,
]
}
}
```
## As a //build/config or //build/toolchain author
The `default_toolchain` is declared in the `BUILDCONFIG.gn` file (in Google
projects this normally is in the `//build/config` directory). Usually the
`default_toolchain` should be the toolchain for the `target_os` and
`target_cpu`. The `current_toolchain` reflects the toolchain that is currently
in effect for a rule.
Be sure you understand the differences between `host_cpu`, `target_cpu`,
`current_cpu`, and `toolchain_cpu` (and the os equivalents). The first
two are set as described above. You are responsible for making sure that
`current_cpu` is set appropriately in your toolchain definitions; if you
are using the stock templates like `gcc_toolchain` and `msvc_toolchain`,
that means you are responsible for making sure that `toolchain_cpu` and
`toolchain_os` are set as appropriate in the template invocations.

236
docs/faq.md Normal file
View File

@ -0,0 +1,236 @@
# GN Frequently Asked Questions
[TOC]
## Where is the GN documentation?
GN has extensive built-in help, so you can run `gn help`, but you can also see
all of the help on [the reference page](reference.md). See also the [quick
start](quick_start.md) guide and the [language and operation
details](language.md).
## Can I generate XCode or Visual Studio projects?
You can generate skeleton (or wrapper) projects for Xcode, Visual Studio,
QTCreator, and Eclipse that will list the files and targets in the build, but
use Ninja to do the actual build. You cannot generate "real" projects that look
and compile like native ones.
Run `gn help gen` for more details.
## How do I do cross-compiles?
GN has robust support for doing cross compiles and building things for
multiple architectures in a single build.
See [GNCrossCompiles](cross_compiles.md) for more info.
## Can I control what targets are built by default?
Yes! If you create a group target called "default" in the top-level (root) build
file, i.e., "//:default", GN will tell Ninja to build that by default, rather
than building everything.
## Are there any public presentations on GN?
[There's at least one](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing), from 2015. There
haven't been big changes since then apart from moving it to a standalone
repo, so it should still be relevant.
## What is the order of flags and values given to the compiler?
The final values of compiler flags, linker flags, defines, and include
directories are collected from various sources by GN. The ordering is defined
as:
1. Those set directly on the current target (not in a config).
2. Those set on the `configs` on the target in order that the configs appear in the list.
3. Those set on the `all_dependent_configs` on the target in order that the configs appear in the list.
4. Those set on the `public_configs` on the target in order that those configs appear in the list.
5. `all_dependent_configs` pulled from dependencies, in the order of the `deps` list. This is done recursively. If a config appears more than once, only the first occurrence will be used.
6. `public_configs` pulled from dependencies, in the order of the `deps` list. If a dependency is public, they will be applied recursively.
If you need a specific relative ordering of values you may need to put those
flags in a config and prepend or append that config in a way that produces the
desired result.
## How can a target affect those that depend on it?
The main way that information flows up the dependency graph is via
`public_configs`. This allows a target to add preprocessor defines, compiler
flags, and linker flags to targets that depend on it. A typical example is a
library that requires `include_dirs` and `defines` to be set in a certain way
for its headers to work. It would put its values in a config:
```
config("icu_config") {
include_dirs = [ "//third_party/icu" ]
defines = [ "U_USING_ICU_NAMESPACE=0" ]
}
```
The library would then reference that as a `public_config` which will apply it
to any target that directly depends on the `icu` target:
```
shared_library("icu") {
sources = [ ... ]
deps = [ ... ]
public_configs = [ ":icu_config" ] # Label of config defined above.
}
```
A `public_config` applies only to direct dependencies of the target. If a target
wants to "republish" the `public_configs` from its dependencies, it would list
those dependencies in its `public_deps`. In this example, a "browser" library
might use ICU headers in its own headers, so anything that depends on it also
needs to get the ICU configuration:
```
shared_library("browser") {
...
# Anything that depends on this "browser" library will also get ICU's settings.
public_deps = [ "//third_party/icu" ]
}
```
Another way apply settings up the dependency graph is with
`all_dependent_configs` which works like `public_configs` except that it is
applied to all dependent targets regardless of `deps`/`public_deps`. Use of this
feature is discouraged because it is easy to accumulate lots of unnecessary
settings in a large project. Ideally all targets can define which information
their dependencies need and can control this explicitly with `public_deps`.
The last way that information can be collected across the dependency graph is
with the metadata feature. This allows data (see `gn help metadata`) to be
collected from targets to be written to disk (see `gn help generated_file`) for
the build to use. Computed metadata values are written after all BUILD.gn files
are processed and are not available to the GN script.
Sometimes people want to write conditional GN code based on values of a
dependency. This is not possible: GN has no defined order for loading BUILD.gn
files (this allows pararellism) so GN may not have even loaded the file
containing a dependency when you might want information about it. The only way
information flows around the dependency graph is if GN itself is propagating
that data after the targets are defined.
## How can a target affect its dependencies?
Sometimes you might have a dependency graph **A 🠲 Z** or a longer chain **A 🠲 B
🠲 C 🠲 Z** and want to control some aspect of **Z** when used from **A**. This is
not possible in GN: information only flows up the dependency chain.
Every label in GN is compiled once per _toolchain_. This means that every target
that depends on **B** gets the same version of **B**. If you need different
variants of **B** there are only two options:
1. Explicitly define two similar but differently named targets encoding the
variations you need. This can be done without specifying everything twice using
a template or by writing things like the sources to a variable and using that
variable in each version of the target.
2. Use different toolchains. This is commonly used to encode "host" versus
"target" differences or to compile parts of a project with something like ASAN.
It is possible to use toolchains to encode any variation you might desire but
this can be difficult to manage and might be impossible or discoraged depending
on how your project is set up (Chrome and Fuchsia use toolchains for specific
purposes only).
## How can I recursively copy a directory as a build step?
Sometimes people want to write a build action that expresses copying all files
(possibly recursively, possily not) from a source directory without specifying
all files in that directory in a BUILD file. This is not possible to express:
correct builds must list all inputs. Most approaches people try to work around
this break in some way for incremental builds, either the build step is run
every time (the build is always "dirty"), file modifications will be missed, or
file additions will be missed.
One thing people try is to write an action that declares an input directory and
an output directory and have it copy all files from the source to the
destination. But incremental builds are likely going to be incorrect if you do
this. Ninja determines if an output is in need of rebuilding by comparing the
last modified date of the source to the last modified date of the destination.
Since almost no filesystems propagate the last modified date of files to their
directory, modifications to files in the source will not trigger an incremental
rebuild.
Beware when testing this: most filesystems update the last modified date of the
parent directory (but not recursive parents) when adding to or removing a file
from that directory so this will appear to work in many cases. But no modern
production filesystems propagate modification times of the contents of the files
to any directories because it would be very slow. The result will be that
modifications to the source files will not be reflected in the output when doing
incremental builds.
Another thing people try is to write all of the source files to a "depfile" (see
`gn help depfile`) and to write a single stamp file that tracks the modified
date of the output. This approach also may appear to work but is subtly wrong:
the additions of new files to the source directory will not trigger the build
step and that addition will not be reflected in an incremental build.
## How can I reference all files in a directory (glob)?
Sometimes people want to automatically refer to all files in a directory,
typically something like `"*.cc"` for the sources. This is called a "glob."
Globs are not supported in GN. In order for Ninja to know when to re-run
GN, it would need to check the directory modification times of any
directories being globbed. Directory modification times that reflect
additions and removals of files are not as reliably implemented across
platforms and filesystems as file modification times (for example, it is
not supported on Windows FAT32 drives).
Even if directory modification times work properly on your build systems,
GN's philosophy prefers a very explicit build specification style that
is contrary to globs.
## Why does "gn check" complain about conditionally included headers?
The "gn check" feature (see `gn help check`) validates that the source code's
use of header files follows the requirements set up in the build. It can be a
very useful tool for ensuring build correctness.
GN scans the source code for `#include` directives and checks that the included
files are allowed given the specification of the build. But it is relatively
simplistic and does not understand the preprocessor. This means that some
headers that are correctly included for a different build variant might be
flagged by GN. To disable checking of an include, append a "nogncheck"
annotation to the include line:
```
#if defined(OS_ANDROID)
#include "src/android/foo/bar.h" // nogncheck
#endif
```
Correctly handling these cases requires a full preprocessor implementation
because many preprocessor conditions depend on values set by other headers.
Implementing this would require new code and complexity to define the toolchain
and run the preprocessor, and also means that a full build be done before doing
the check (since some headers will be generated at build-time). So far, the
complexity and disadvantages have outweighed the advantages of a perfectly
correct "gn check" implementation.
## Why does "gn check" miss my header?
The "gn check" feature (see previous question) only checks for headers that have
been declared in the current toolchain. So if your header never appears in a
`sources` or `public` list, any file will be able to include it without "gn
check" failing. As a result, targets should always list all headers they contain
even though listing them does not affect the build.
Sometimes a feature request is made to flag unknown headers so that people will
know they should be added to the build. But the silent omission of headers
outside of the current toolchain is an important feature that limits the
necessity of "nogncheck" annotations (see previous question).
In a large project like Chrome, many platform-specific headers will only be
defined in that platform's build (for example, Android-specific headers would
only be listed in the build when compiling for Android). Because the checking
doesn't understand the preprocessor, checking unknown files would flag uses of
these headers even if they were properly guarded by platform conditionals. By
ignoring headers outside of the current toolchain, the "nogncheck" annotations
can be omitted for most platform-specific files.

535
docs/language.md Normal file
View File

@ -0,0 +1,535 @@
# GN Language and Operation
[TOC]
## Introduction
This page describes many of the language details and behaviors.
### Use the built-in help!
GN has an extensive built-in help system which provides a reference for
every function and built-in variable. This page is more high-level.
```
gn help
```
You can also see the
[slides](https://docs.google.com/presentation/d/15Zwb53JcncHfEwHpnG_PoIbbzQ3GQi_cpujYwbpcbZo/edit?usp=sharing)
from a March, 2016 introduction to GN. The speaker notes contain the full
content.
### Design philosophy
* Writing build files should not be a creative endeavour. Ideally two
people should produce the same buildfile given the same
requirements. There should be no flexibility unless it's absolutely
needed. As many things should be fatal errors as possible.
* The definition should read more like code than rules. I don't want
to write or debug Prolog. But everybody on our team can write and
debug C++ and Python.
* The build language should be opinionated as to how the build should
work. It should not necessarily be easy or even possible to express
arbitrary things. We should be changing source and tooling to make
the build simpler rather than making everything more complicated to
conform to external requirements (within reason).
* Be like Blaze when it makes sense (see "Differences and similarities
to Blaze" below).
## Language
GN uses an extremely simple, dynamically typed language. The types are:
* Boolean (`true`, `false`).
* 64-bit signed integers.
* Strings.
* Lists (of any other types).
* Scopes (sort of like a dictionary, only for built-in stuff).
There are some built-in variables whose values depend on the current
environment. See `gn help` for more.
There are purposefully many omissions in the language. There are no
user-defined function calls, for example (templates are the closest thing). As
per the above design philosophy, if you need this kind of thing you're probably
doing it wrong.
The full grammar for language nerds is available in `gn help grammar`.
### Strings
Strings are enclosed in double-quotes and use backslash as the escape
character. The only escape sequences supported are:
* `\"` (for literal quote)
* `\$` (for literal dollars sign)
* `\\` (for literal backslash)
Any other use of a backslash is treated as a literal backslash. So, for
example, `\b` used in patterns does not need to be escaped, nor do most Windows
paths like `"C:\foo\bar.h"`.
Simple variable substitution is supported via `$`, where the word
following the dollars sign is replaced with the value of the variable.
You can optionally surround the name with `{}` if there is not a
non-variable-name character to terminate the variable name. More complex
expressions are not supported, only variable name substitution.
```
a = "mypath"
b = "$a/foo.cc" # b -> "mypath/foo.cc"
c = "foo${a}bar.cc" # c -> "foomypathbar.cc"
```
You can encode 8-bit characters using "$0xFF" syntax, so a string with newlines
(hex 0A) would `"look$0x0Alike$0x0Athis"`.
### Lists
Aside from telling empty lists from non empty lists (`a == []`), there is no
way to get the length of a list. If you find yourself wanting to do this kind
of thing, you're trying to do too much work in the build.
Lists support appending:
```
a = [ "first" ]
a += [ "second" ] # [ "first", "second" ]
a += [ "third", "fourth" ] # [ "first", "second", "third", "fourth" ]
b = a + [ "fifth" ] # [ "first", "second", "third", "fourth", "fifth" ]
```
Appending a list to another list appends the items in the second list
rather than appending the list as a nested member.
You can remove items from a list:
```
a = [ "first", "second", "third", "first" ]
b = a - [ "first" ] # [ "second", "third" ]
a -= [ "second" ] # [ "first", "third", "first" ]
```
The - operator on a list searches for matches and removes all matching
items. Subtracting a list from another list will remove each item in the
second list.
If no matching items are found, an error will be thrown, so you need to
know in advance that the item is there before removing it. Given that
there is no way to test for inclusion, the main use-case is to set up a
master list of files or flags, and to remove ones that don't apply to
the current build based on various conditions.
Stylistically, prefer to only add to lists and have each source file or
dependency appear once. This is the opposite of the advice Chrome-team used to
give for GYP (GYP would prefer to list all files, and then remove the ones you
didn't want in conditionals).
Lists support zero-based subscripting to extract values:
```
a = [ "first", "second", "third" ]
b = a[1] # -> "second"
```
The \[\] operator is read-only and can not be used to mutate the
list. The primary use-case of this is when an external script returns
several known values and you want to extract them.
There are some cases where it's easy to overwrite a list when you mean
to append to it instead. To help catch this case, it is an error to
assign a nonempty list to a variable containing an existing nonempty
list. If you want to get around this restriction, first assign the
destination variable to the empty list.
```
a = [ "one" ]
a = [ "two" ] # Error: overwriting nonempty list with a nonempty list.
a = [] # OK
a = [ "two" ] # OK
```
Note that execution of the build script is done without intrinsic
knowledge of the meaning of the underlying data. This means that it
doesn't know that `sources` is a list of file names, for example. So if
you remove an item, it must match the literal string rather than
specifying a different name that will resolve to the same file name.
### Conditionals
Conditionals look like C:
```
if (is_linux || (is_win && target_cpu == "x86")) {
sources -= [ "something.cc" ]
} else if (...) {
...
} else {
...
}
```
You can use them in most places, even around entire targets if the
target should only be declared in certain circumstances.
### Looping
You can iterate over a list with `foreach`. This is discouraged. Most things
the build should do can normally be expressed without doing this, and if you
find it necessary it may be an indication you're doing too much work in the
metabuild.
```
foreach(i, mylist) {
print(i) # Note: i is a copy of each element, not a reference to it.
}
```
### Function calls
Simple function calls look like most other languages:
```
print("hello, world")
assert(is_win, "This should only be executed on Windows")
```
Such functions are built-in and the user can not define new ones.
Some functions take a block of code enclosed by `{ }` following them:
```
static_library("mylibrary") {
sources = [ "a.cc" ]
}
```
Most of these define targets. The user can define new functions like this
with the template mechanism discussed below.
Precisely, this expression means that the block becomes an argument to the
function for the function to execute. Most of the block-style functions execute
the block and treat the resulting scope as a dictionary of variables to read.
### Scoping and execution
Files and function calls followed by `{ }` blocks introduce new scopes. Scopes
are nested. When you read a variable, the containing scopes will be searched in
reverse order until a matching name is found. Variable writes always go to the
innermost scope.
There is no way to modify any enclosing scope other than the innermost
one. This means that when you define a target, for example, nothing you
do inside of the block will "leak out" into the rest of the file.
`if`/`else`/`foreach` statements, even though they use `{ }`, do not introduce
a new scope so changes will persist outside of the statement.
## Naming things
### File and directory names
File and directory names are strings and are interpreted as relative to
the current build file's directory. There are three possible forms:
Relative names:
```
"foo.cc"
"src/foo.cc"
"../src/foo.cc"
```
Source-tree absolute names:
```
"//net/foo.cc"
"//base/test/foo.cc"
```
System absolute names (rare, normally used for include directories):
```
"/usr/local/include/"
"/C:/Program Files/Windows Kits/Include"
```
## Build configuration
## Targets
A target is a node in the build graph. It usually represents some kind
of executable or library file that will be generated. Targets depend on
other targets. The built-in target types (see `gn help <targettype>` for
more help) are:
* `action`: Run a script to generate a file.
* `action_foreach`: Run a script once for each source file.
* `bundle_data`: Declare data to go into a Mac/iOS bundle.
* `create_bundle`: Creates a Mac/iOS bundle.
* `executable`: Generates an executable file.
* `group`: A virtual dependency node that refers to one or more other
targets.
* `shared_library`: A .dll or .so.
* `loadable_module`: A .dll or .so loadable only at runtime.
* `source_set`: A lightweight virtual static library (usually
preferrable over a real static library since it will build faster).
* `static_library`: A .lib or .a file (normally you'll want a
`source_set` instead).
You can extend this to make custom target types using templates (see below). In
Chrome some of the more commonly-used templates are:
* `component`: Either a source set or shared library, depending on the
build type.
* `test`: A test executable. On mobile this will create the appropriate
native app type for tests.
* `app`: Executable or Mac/iOS application.
* `android_apk`: Make an APK. There are a _lot_ of other Android ones, see
`//build/config/android/rules.gni`.
## Configs
Configs are named objects that specify sets of flags, include
directories, and defines. They can be applied to a target and pushed to
dependent targets.
To define a config:
```
config("myconfig") {
includes = [ "src/include" ]
defines = [ "ENABLE_DOOM_MELON" ]
}
```
To apply a config to a target:
```
executable("doom_melon") {
configs = [ ":myconfig" ]
}
```
It is common for the build config file to specify target defaults that
set a default list of configs. Targets can add or remove to this list as
needed. So in practice you would usually use `configs += ":myconfig"` to
append to the list of defaults.
See `gn help config` for more information about how configs are declared
and applied.
### Public configs
A target can apply settings to other targets that depend on it. The most
common example is a third party target that requires some defines or
include directories for its headers to compile properly. You want these
settings to apply both to the compile of the third party library itself,
as well as all targets that use the library.
To do this, you write a config with the settings you want to apply:
```
config("my_external_library_config") {
includes = "."
defines = [ "DISABLE_JANK" ]
}
```
Then this config is added to the target as a "public" config. It will
apply both to the target as well as targets that directly depend on it.
```
shared_library("my_external_library") {
...
# Targets that depend on this get this config applied.
public_configs = [ ":my_external_library_config" ]
}
```
Dependent targets can in turn forward this up the dependency tree
another level by adding your target as a "public" dependency.
```
static_library("intermediate_library") {
...
# Targets that depend on this one also get the configs from "my external library".
public_deps = [ ":my_external_library" ]
}
```
A target can forward a config to all dependents until a link boundary is
reached by setting it as an `all_dependent_config`. This is strongly
discouraged as it can spray flags and defines over more of the build than
necessary. Instead, use public_deps to control which flags apply where.
In Chrome, prefer the build flag header system (`build/buildflag_header.gni`)
for defines which prevents most screw-ups with compiler defines.
## Templates
Templates are GN's primary way to re-use code. Typically, a template
would expand to one or more other target types.
```
# Declares a script that compiles IDL files to source, and then compiles those
# source files.
template("idl") {
# Always base helper targets on target_name so they're unique. Target name
# will be the string passed as the name when the template is invoked.
idl_target_name = "${target_name}_generate"
action_foreach(idl_target_name) {
...
}
# Your template should always define a target with the name target_name.
# When other targets depend on your template invocation, this will be the
# destination of that dependency.
source_set(target_name) {
...
deps = [ ":$idl_target_name" ] # Require the sources to be compiled.
}
}
```
Typically your template definition would go in a `.gni` file and users
would import that file to see the template definition:
```
import("//tools/idl_compiler.gni")
idl("my_interfaces") {
sources = [ "a.idl", "b.idl" ]
}
```
Declaring a template creates a closure around the variables in scope at
that time. When the template is invoked, the magic variable `invoker` is
used to read variables out of the invoking scope. The template would
generally copy the values its interested in into its own scope:
```
template("idl") {
source_set(target_name) {
sources = invoker.sources
}
}
```
The current directory when a template executes will be that of the
invoking build file rather than the template source file. This is so
files passed in from the template invoker will be correct (this
generally accounts for most file handling in a template). However, if
the template has files itself (perhaps it generates an action that runs
a script), you will want to use absolute paths ("//foo/...") to refer to
these files to account for the fact that the current directory will be
unpredictable during invocation. See `gn help template` for more
information and more complete examples.
## Other features
### Imports
You can import `.gni` files into the current scope with the `import`
function. This is _not_ an include in the C++ sense. The imported file is
executed independently and the resulting scope is copied into the current file
(C++ executes the included file in the current context of when the
include directive appeared). This allows the results of the import to be
cached, and also prevents some of the more "creative" uses of includes like
multiply-included files.
Typically, a `.gni` would define build arguments and templates. See `gn
help import` for more.
Your `.gni` file can define temporary variables that are not exported files
that include it by using a preceding underscore in the name like `_this`.
### Path processing
Often you will want to make a file name or a list of file names relative
to a different directory. This is especially common when running
scripts, which are executed with the build output directory as the
current directory, while build files usually refer to files relative to
their containing directory.
You can use `rebase_path` to convert directories. See `gn help
rebase_path` for more help and examples. Typical usage to convert a file
name relative to the current directory to be relative to the root build
directory would be: ``` new_paths = rebase_path("myfile.c",
root_build_dir) ```
### Patterns
Patterns are used to generate the output file names for a given set of
inputs for custom target types, and to automatically remove files from
the list values (see `gn help filter_include` and `gn help filter_exclude`).
They are like simple regular expressions. See `gn help label_pattern`
for more.
### Executing scripts
There are two ways to execute scripts. All external scripts in GN are in
Python. The first way is as a build step. Such a script would take some
input and generate some output as part of the build. Targets that invoke
scripts are declared with the "action" target type (see `gn help
action`).
The second way to execute scripts is synchronously during build file
execution. This is necessary in some cases to determine the set of files
to compile, or to get certain system configurations that the build file
might depend on. The build file can read the stdout of the script and
act on it in different ways.
Synchronous script execution is done by the `exec_script` function (see
`gn help exec_script` for details and examples). Because synchronously
executing a script requires that the current buildfile execution be
suspended until a Python process completes execution, relying on
external scripts is slow and should be minimized.
To prevent abuse, files permitted to call `exec_script` can be whitelisted in
the toplevel `.gn` file. Chrome does this to require additional code review
for such additions. See `gn help dotfile`.
You can synchronously read and write files which is discouraged but
occasionally necessary when synchronously running scripts. The typical use-case
would be to pass a list of file names longer than the command-line limits of
the current platform. See `gn help read_file` and `gn help write_file` for how
to read and write files. These functions should be avoided if at all possible.
Actions that exceed command-line length limits can use response files to
get around this limitation without synchronously writing files. See
`gn help response_file_contents`.
# Differences and similarities to Blaze
Blaze is Google's internal build system, now publicly released as
[Bazel](http://bazel.io/). It has inspired a number of other systems such as
[Pants](http://www.pantsbuild.org/) and [Buck](http://facebook.github.io/buck/).
In Google's homogeneous environment, the need for conditionals is very
low and they can get by with a few hacks (`abi_deps`). Chrome uses
conditionals all over the place and the need to add these is the main
reason for the files looking different.
GN also adds the concept of "configs" to manage some of the trickier
dependency and configuration problems which likewise don't arise on the
server. Blaze has a concept of a "configuration" which is like a GN
toolchain, but built into the tool itself. The way that toolchains work
in GN is a result of trying to separate this concept out into the build
files in a clean way.
GN keeps some GYP concept like "all dependent" settings which work a bit
differently in Blaze. This is partially to make conversion from the existing
GYP code easier, and the GYP constructs generally offer more fine-grained
control (which is either good or bad, depending on the situation).
GN also uses GYP names like "sources" instead of "srcs" since
abbreviating this seems needlessly obscure, although it uses Blaze's
"deps" since "dependencies" is so hard to type. Chromium also compiles
multiple languages in one target so specifying the language type on the
target name prefix was dropped (e.g. from `cc_library`).

344
docs/quick_start.md Normal file
View File

@ -0,0 +1,344 @@
# GN Quick Start guide
[TOC]
## Running GN
You just run `gn` from the command line. For large projects, GN is versioned
and distributed with the source checkout.
* For Chromium and Chromium-based projects, there is a script in
`depot_tools`, which is presumably in your PATH, with this name. The script
will find the binary in the source tree containing the current directory and
run it.
* For Fuchsia in-tree development, run `fx gn ...` which will find the right
GN binary and run it with the given arguments.
* For other projects, see your project's documentation.
## Setting up a build
Unlike some other build systems, with GN you set up your own build directories
with the settings you want. This lets you maintain as many different builds in
parallel as you need.
Once you set up a build directory, the Ninja files will be automatically
regenerated if they're out of date when you build in that directory so you
should not have to re-run GN.
To make a build directory:
```
gn gen out/my_build
```
## Passing build arguments
Set build arguments on your build directory by running:
```
gn args out/my_build
```
This will bring up an editor. Type build args into that file like this:
```
is_component_build = true
is_debug = false
```
The available variables will depend on your build (this example is from
Chromium). You can see the list of available arguments and their default values
by typing
```
gn args --list out/my_build
```
on the command line. Note that you have to specify the build directory
for this command because the available arguments can change according
to the build.
Chrome developers can also read the [Chrome-specific build
configuration](http://www.chromium.org/developers/gn-build-configuration)
instructions for more information.
## Cross-compiling to a target OS or architecture
Run `gn args out/Default` (substituting your build directory as needed) and
add one or more of the following lines for common cross-compiling options.
```
target_os = "chromeos"
target_os = "android"
target_cpu = "arm"
target_cpu = "x86"
target_cpu = "x64"
```
See [GN cross compiles](cross_compiles.md) for more info.
## Step-by-step
### Adding a build file
Go to the directory `examples/simple_build`. This is the root of a minimal GN
repository.
In that directory there is a `tutorial` directory. There is already a
`tutorial.cc` file that's not hooked up to the build. Create a new `BUILD.gn`
file in that directory for our new target.
```
executable("tutorial") {
sources = [
"tutorial.cc",
]
}
```
Now we just need to tell the build about this new target. Open the `BUILD.gn`
file in the parent (`simple_build`) directory. GN starts by loading this root
file, and then loads all dependencies ourward from here, so we just need to add
a reference to our new target from this file.
You could add our new target as a dependency from one of the existing targets in
the `simple_build/BUILD.gn` file, but it usually doesn't make a lot of sense to
have an executable as a depdency of another executable (they can't be linked).
So let's make a "tools" group. In GN, a "group" is just a collection of
dependencies that's not complied or linked:
```
group("tools") {
deps = [
# This will expand to the name "//tutorial:tutorial" which is the full name
# of our new target. Run "gn help labels" for more.
"//tutorial",
]
}
```
### Testing your addition
From the command line in the `simple_build` directory:
```
gn gen out
ninja -C out tutorial
out/tutorial
```
You should see "Hello, world." output to the console.
Side note: GN encourages target names for static libraries that aren't globally
unique. To build one of these, you can pass the label with its path (but no leading
"//") to ninja:
```
ninja -C out some/path/to/target:my_target
```
### Declaring dependencies
Let's look at the targets defined in
[examples/simple_build/BUILD.gn](../examples/simple_build/BUILD.gn). There is a
static library that defines one function, `GetStaticText()`:
```
static_library("hello_static") {
sources = [
"hello_static.cc",
"hello_static.h",
]
}
```
There is also a shared library that defines one function `GetSharedText()`:
```
shared_library("hello_shared") {
sources = [
"hello_shared.cc",
"hello_shared.h",
]
defines = [ "HELLO_SHARED_IMPLEMENTATION" ]
}
```
This also illustrates how to set preprocessor defines for a target. To set more
than one or to assign values, use this form:
```
defines = [
"HELLO_SHARED_IMPLEMENTATION",
"ENABLE_DOOM_MELON=0",
]
```
Now let's look at the executable that depends on these two libraries:
```
executable("hello") {
sources = [
"hello.cc",
]
deps = [
":hello_shared",
":hello_static",
]
}
```
This executable includes one source file and depends on the previous
two libraries. Labels starting with a colon refer to a target with that name in
the current BUILD.gn file.
### Test the binary
From the command line in the `simple_build` directory:
```
ninja -C out hello
out/hello
```
Note that you **didn't** need to re-run GN. GN will automatically rebuild
the ninja files when any build file has changed. You know this happens
when ninja prints `[1/1] Regenerating ninja files` at the beginning of
execution.
### Putting settings in a config
Users of a library often need compiler flags, defines, and include directories
applied to them. To do this, put all such settings into a "config" which is a
named collection of settings (but not sources or dependencies):
```
config("my_lib_config") {
defines = [ "ENABLE_DOOM_MELON" ]
include_dirs = [ "//third_party/something" ]
}
```
To apply a config's settings to a target, add it to the `configs` list:
```
static_library("hello_shared") {
...
# Note "+=" here is usually required, see "default configs" below.
configs += [
":my_lib_config",
]
}
```
A config can be applied to all targets that depend on the current one by putting
its label in the `public_configs` list:
```
static_library("hello_shared") {
...
public_configs = [
":my_lib_config",
]
}
```
The `public_configs` also applies to the current target, so there's no need to
list a config in both places.
### Default configs
The build configuration will set up some settings that apply to every target by
default. These will normally be set as a default list of configs. You can see
this using the "print" command which is useful for debugging:
```
executable("hello") {
print(configs)
}
```
Running GN will print something like:
```
$ gn gen out
["//build:compiler_defaults", "//build:executable_ldconfig"]
Done. Made 5 targets from 5 files in 9ms
```
Targets can modify this list to change their defaults. For example, the build
setup might turn off exceptions by default by adding a `no_exceptions` config,
but a target might re-enable them by replacing it with a different one:
```
executable("hello") {
...
configs -= [ "//build:no_exceptions" ] # Remove global default.
configs += [ "//build:exceptions" ] # Replace with a different one.
}
```
Our print command from above could also be expressed using string interpolation.
This is a way to convert values to strings. It uses the symbol "$" to refer to a
variable:
```
print("The configs for the target $target_name are $configs")
```
## Add a new build argument
You declare which arguments you accept and specify default values via
`declare_args`.
```
declare_args() {
enable_teleporter = true
enable_doom_melon = false
}
```
See `gn help buildargs` for an overview of how this works.
See `gn help declare_args` for specifics on declaring them.
It is an error to declare a given argument more than once in a given scope, so
care should be used in scoping and naming arguments.
## Don't know what's going on?
You can run GN in verbose mode to see lots of messages about what it's
doing. Use `-v` for this.
### The "desc" command
You can run `gn desc <build_dir> <targetname>` to get information about
a given target:
```
gn desc out/Default //foo/bar:say_hello
```
will print out lots of exciting information. You can also print just one
section. Lets say you wanted to know where your `TWO_PEOPLE` define
came from on the `say_hello` target:
```
> gn desc out/Default //foo/bar:say_hello defines --blame
...lots of other stuff omitted...
From //foo/bar:hello_config
(Added by //foo/bar/BUILD.gn:12)
TWO_PEOPLE
```
Another particularly interesting variation:
```
gn desc out/Default //base:base_i18n deps --tree
```
See `gn help desc` for more.

8076
docs/reference.md Normal file

File diff suppressed because it is too large Load Diff

44
docs/standalone.md Normal file
View File

@ -0,0 +1,44 @@
# Introduction
This page is about how to design a project that can build independently
with GN but also be brought into the Chrome build.
GN is in principle no different than GYP in that there is some core
configuration that must be the same between both the standalone build
and the Chrome build. However, GN is much more explicit in its naming
and configuration, so the similarities between the two builds are also
much more explicit and there is less flexibility in how things are
configured.
# What you need for a minimal GN build
Requirements:
* A master build config file. Chrome's is `//build/config/BUILDCONFIG.gn`
* A separate build file for the toolchain definition. It's not a good idea
to put these in a BUILD.gn file shared with any target definitions for
complex reasons. Chrome's are in `//build/toolchain/<platform>/BUILD.gn`.
* A `BUILD.gn` file in the root directory. This will be loaded after the
build config file to start the build.
You may want a `.gn` file in the root directory. When you run GN it
recursively looks up the directory tree until it finds this file, and it
treats the containing directory as the "source root". This file also
defines the location of the master build config file:
* See Chrome's `src/.gn` file.
* Unlike Chrome, you probably don't need to define a secondary root.
* see `gn help dotfile` for more.
Adding a `.gn` file in a repository that is pulled into Chrome means
that then running GN in your subdirectory will configure a build for
your subproject rather than for all of Chrome. This could be an
advantage or a disadvantage.
If you are in a directory with such a file and you want to not use it
(e.g., to do the full Chrome build instead), you can use the command-line
flags `--root` and `--dotfile` to set the values you want.
If you want a completely standalone build that has nothing to do with Chrome
and doesn't use Chrome's `//build` files, you can look at an example in
[//tools/gn/example](../tools/gn/example).

306
docs/style_guide.md Normal file
View File

@ -0,0 +1,306 @@
# GN Style Guide
[TOC]
## Naming and ordering within the file
### Location of build files
It usually makes sense to have more build files closer to the code than
fewer ones at the top level; this is in contrast with what we did with
GYP. This makes things easier to find, and also makes the set of owners
required for reviews smaller since changes are more focused to particular
subdirectories.
### Targets
* Most BUILD files should have a target with the same name as the
directory. This target should be the first target.
* Other targets should be in some logical order -- usually
more important targets will be first, and unit tests will follow the
corresponding target. If there's no clear ordering, consider
alphabetical order.
* Test support libraries should be static libraries named "test\_support".
For example, "//ui/compositor:test\_support". Test support libraries should
include as public deps the non-test-support version of the library
so tests need only depend on the test\_support target (rather than
both).
Naming advice
* Targets and configs should be named using lowercase with underscores
separating words, unless there is a strong reason to do otherwise.
* Source sets, groups, and static libraries do not need globally unique names.
Prefer to give such targets short, non-redundant names without worrying
about global uniqueness. For example, it looks much better to write a
dependency as `"//mojo/public/bindings"` rather than
`"//mojo/public/bindings:mojo_bindings"`
* Shared libraries (and by extension, components) must have globally unique
output names. Give such targets short non-unique names above, and then
provide a globally unique `output_name` for that target.
* Executables and tests should be given a globally unique name. Technically
only the output names must be unique, but since only the output names
appear in the shell and on bots, it's much less confusing if the name
matches the other places the executable appears.
### Configs
* A config associated with a single target should be named the same as
the target with `_config` following it.
* A config should appear immediately before the corresponding target
that uses it.
### Example
Example for the `src/foo/BUILD.gn` file:
```
# Copyright 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Config for foo is named foo_config and immediately precedes it in the file.
config("foo_config") {
}
# Target matching path name is the first target.
executable("foo") {
}
# Test for foo follows it.
test("foo_unittests") {
}
config("bar_config") {
}
source_set("bar") {
}
```
## Ordering within a target
1. `output_name` / `visibility` / `testonly`
2. `sources`
3. `cflags`, `include_dirs`, `defines`, `configs` etc. in whatever
order makes sense to you.
4. `public_deps`
5. `deps`
### Conditions
Simple conditions affecting just one variable (e.g. adding a single
source or adding a flag for one particular OS) can go beneath the
variable they affect. More complicated conditions affecting more than
one thing should go at the bottom.
Conditions should be written to minimize the number of conditional blocks.
## Formatting and indenting
GN contains a built-in code formatter which defines the formatting style.
Some additional notes:
* Variables are `lower_case_with_underscores`.
* Comments should be complete sentences with periods at the end.
* Compiler flags and such should always be commented with what they do
and why the flag is needed.
### Sources
Prefer to list sources only once. It is OK to conditionally include sources
rather than listing them all at the top and then conditionally excluding them
when they don't apply. Conditional inclusion is often clearer since a file is
only listed once and it's easier to reason about when reading.
```
sources = [
"main.cc",
]
if (use_aura) {
sources += [ "thing_aura.cc" ]
}
if (use_gtk) {
sources += [ "thing_gtk.cc" ]
}
```
### Deps
* Deps should be in alphabetical order.
* Deps within the current file should be written first and not
qualified with the file name (just `:foo`).
* Other deps should always use fully-qualified path names unless
relative ones are required for some reason.
```
deps = [
":a_thing",
":mystatic",
"//foo/bar:other_thing",
"//foo/baz:that_thing",
]
```
### Import
Use fully-qualified paths for imports:
```
import("//foo/bar/baz.gni") # Even if this file is in the foo/bar directory
```
## Usage
### Source sets versus static libraries
Source sets and static libraries can be used interchangeably in most cases. If
you're unsure what to use, a source set is almost never wrong and is less likely
to cause problems, but on a large project using the right kind of target can
be important, so you should know about the following tradeoffs.
Static libraries follow different linking rules. When a static library is
included in a link, only the object files that contain unresolved symbols will
be brought into the build. Source sets result in every object file being added
to the link line of the final binary.
* If you're eventually linking code into a component, shared library, or
loadable module, you normally need to use source sets. This is because
object files with no symbols referenced from within the shared library will
not be linked into the final library at all. This omission will happen even
if that object file has a symbol marked for export that targets dependent
on that shared library need. This will result in undefined symbols when
linking later targets.
* Unit tests (and anything else with static initializers with side effects)
must use source sets. The gtest TEST macros create static initializers
that register the test. But since no code references symbols in the object
file, linking a test into a static library and then into a test executable
means the tests will get stripped.
* On some platforms, static libraries may involve duplicating all of the
data in the object files that comprise it. This takes more disk space and
for certain very large libraries in configurations with very large object
files can cause internal limits on the size of static libraries to be
exceeded. Source sets do not have this limitation. Some targets switch
between source sets and static libraries depending on the build
configuration to avoid this problem. Some platforms (or toolchains) may
support something called "thin archives" which don't have this problem;
but you can't rely on this as a portable solution.
* Source sets can have no sources, while static libraries will give strange
platform-specific errors if they have no sources. If a target has only
headers (for include checking purposes) or conditionally has no sources on
some platforms, use a source set.
* In cases where a lot of the symbols are not needed for a particular link
(this especially happens when linking test binaries), putting that code in
a static library can dramatically increase linking performance. This is
because the object files not needed for the link are never considered in
the first place, rather than forcing the linker to strip the unused code
in a later pass when nothing references it.
### Components versus shared libraries versus source sets
A component is a Chrome template (rather than a built-in GN concept) that
expands either to a shared library or a static library / source set depending
on the value of the `is_component_build` variable. This allows release builds
to be linked statically in a large binary, but for developers to use shared
libraries for most operations. Chrome developers should almost always use
a component instead of shared library directly.
Much like the source set versus static library tradeoff, there's no hard
and fast rule as to when you should use a component or not. Using
components can significantly speed up incremental builds by making
linking much faster, but they require you to have to think about which
symbols need to be exported from the target.
### Loadable modules versus shared libraries
A shared library will be listed on the link line of dependent targets and will
be loaded automatically by the operating system when the application starts
and symbols automatically resolved. A loadable module will not be linked
directly and the application must manually load it.
On Windows and Linux shared libraries and loadable modules result in the same
type of file (`.dll` and `.so`, respectively). The only difference is in how
they are linked to dependent targets. On these platforms, having a `deps`
dependency on a loadable module is the same as having a `data_deps`
(non-linked) dependency on a shared library.
On Mac, these targets have different formats: a shared library will generate a
`.dylib` file and a loadable module will generate a `.so` file.
Use loadable modules for things like plugins. In the case of plugin-like
libraries, it's good practice to use both a loadable module for the target type
(even for platforms where it doesn't matter) and data deps for targets that
depend on it so it's clear from both places that how the library will be linked
and loaded.
## Build arguments
### Scope
Build arguments should be scoped to a unit of behavior, e.g. enabling a feature.
Typically an argument would be declared in an imported file to share it with
the subset of the build that could make use of it.
Chrome has many legacy flags in `//build/config/features.gni`,
`//build/config/ui.gni`. These locations are deprecated. Feature flags should
go along with the code for the feature. Many browser-level features can go
somewhere in `//chrome/` without lower-level code knowing about it. Some
UI environment flags can go into `//ui/`, and many flags can also go with
the corresponding code in `//components/`. You can write a `.gni` file in
components and have build files in chrome or content import it if necessary.
The way to think about things in the `//build` directory is that this is
DEPSed into various projects like V8 and WebRTC. Build flags specific to
code outside of the build directory shouldn't be in the build directory, and
V8 shouldn't get feature defines for Chrome features.
New feature defines should use the buildflag system. See
`//build/buildflag_header.gni` which allows preprocessor defines to be
modularized without many of the disadvantages that made us use global defines
in the past.
### Type
Arguments support all the [GN language types](language.md#Language).
In the vast majority of cases `boolean` is the preferred type, since most
arguments are enabling or disabling features or includes.
`String`s are typically used for filepaths. They are also used for enumerated
types, though `integer`s are sometimes used as well.
### Naming conventions
While there are no hard and fast rules around argument naming there are
many common conventions. If you ever want to see the current list of argument
names and default values for your current checkout use
`gn args out/Debug --list --short`.
`use_foo` - indicates dependencies or major codepaths to include (e.g.
`use_open_ssl`, `use_ozone`, `use_cups`)
`enable_foo` - indicates feature or tools to be enabled (e.g.
`enable_google_now`, `enable_nacl`, `enable_remoting`, `enable_pdf`)
`disable_foo` - _NOT_ recommended, use `enable_foo` instead with swapped default
value
`is_foo` - usually a global state descriptor (e.g. `is_chrome_branded`,
`is_desktop_linux`); poor choice for non-globals
`foo_use_bar` - prefixes can be used to indicate a limited scope for an argument
(e.g. `rtc_use_h264`, `v8_use_snapshot`)
#### Variables
Prefix top-level local variables within `.gni` files with an underscore. This
prefix causes variables to be unavailable to importing scripts.
```
_this_var_will_not_be_exported = 1
but_this_one_will = 2
```

1
examples/ios/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/out/

6
examples/ios/.gn Normal file
View File

@ -0,0 +1,6 @@
# The location of the build configuration file.
buildconfig = "//build/BUILDCONFIG.gn"
# The build script are using `python3` which is the only version of
# python installed by default on macOS Monterey and above.
script_executable = "python3"

10
examples/ios/BUILD.gn Normal file
View File

@ -0,0 +1,10 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
group("all") {
deps = [
"//app:hello",
"//host:username",
]
}

View File

@ -0,0 +1,9 @@
// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end

View File

@ -0,0 +1,32 @@
// Copyright 2019 The Chromium 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 "app/AppDelegate.h"
#import "app/Foo.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
NSLog(@"%@", [[[FooWrapper alloc] init] helloWithName:@"World"]);
return YES;
}
#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration*)application:(UIApplication*)application
configurationForConnectingSceneSession:
(UISceneSession*)connectingSceneSession
options:(UISceneConnectionOptions*)options {
return
[[UISceneConfiguration alloc] initWithName:@"Default Configuration"
sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication*)application
didDiscardSceneSessions:(NSSet<UISceneSession*>*)sceneSessions {
}
@end

67
examples/ios/app/BUILD.gn Normal file
View File

@ -0,0 +1,67 @@
# Copyright 2019 The Chromium 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/config/ios/templates/ios_app_bundle.gni")
import("//build/config/ios/templates/storyboards.gni")
ios_app_bundle("hello") {
output_name = "Hello"
info_plist = "resources/Info.plist"
sources = [
"AppDelegate.h",
"AppDelegate.m",
"SceneDelegate.h",
"SceneDelegate.m",
"ViewController.h",
"ViewController.m",
"main.m",
]
frameworks = [
"CoreGraphics.framework",
"Foundation.framework",
"UIKit.framework",
]
deps = [
":foo",
":storyboards",
"//shared:hello_framework",
"//shared:hello_framework+bundle",
]
}
storyboards("storyboards") {
sources = [
"resources/LaunchScreen.storyboard",
"resources/Main.storyboard",
]
}
source_set("baz") {
module_name = "Baz"
sources = [ "Baz.swift" ]
}
source_set("bar") {
module_name = "Bar"
sources = [ "Bar.swift" ]
deps = [ ":baz" ]
}
group("bar_indirect") {
public_deps = [ ":bar" ]
}
source_set("foo") {
module_name = "Foo"
bridge_header = "Foo-Bridging-Header.h"
sources = [
"Foo.swift",
"FooWrapper.swift",
]
deps = [ ":bar_indirect" ]
}

View File

@ -0,0 +1,8 @@
import Baz;
public class Greeter {
public static func greet(greeting: String, name: String, from: String) -> String {
return greeting + ", " + name + " (from " + from + ")";
}
}

View File

@ -0,0 +1,2 @@
class Baz {}

View File

View File

@ -0,0 +1,12 @@
import Bar
class Foo {
var name: String;
public init(name: String) {
self.name = name;
}
public func hello(name: String) -> String {
return Greeter.greet(greeting: "Hello", name: name, from: self.name);
}
}

View File

@ -0,0 +1,10 @@
import Foundation;
@objc
public class FooWrapper : NSObject {
@objc
public func hello(name: String) -> String {
return Foo(name: "Foo").hello(name: name);
}
}

View File

@ -0,0 +1,11 @@
// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@property(strong, nonatomic) UIWindow* window;
@end

View File

@ -0,0 +1,29 @@
// Copyright 2019 The Chromium 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 "app/SceneDelegate.h"
@implementation SceneDelegate
- (void)scene:(UIScene*)scene
willConnectToSession:(UISceneSession*)session
options:(UISceneConnectionOptions*)connectionOptions {
}
- (void)sceneDidDisconnect:(UIScene*)scene {
}
- (void)sceneDidBecomeActive:(UIScene*)scene {
}
- (void)sceneWillResignActive:(UIScene*)scene {
}
- (void)sceneWillEnterForeground:(UIScene*)scene {
}
- (void)sceneDidEnterBackground:(UIScene*)scene {
}
@end

View File

@ -0,0 +1,9 @@
// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end

View File

@ -0,0 +1,31 @@
// Copyright 2019 The Chromium 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 <HelloShared/HelloShared.h>
#import "app/ViewController.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UILabel* label = [self labelWithText:[Greetings greet]];
[self addCenteredView:label toParentView:self.view];
}
- (UILabel*)labelWithText:(NSString*)text {
UILabel* label = [[UILabel alloc] initWithFrame:CGRectZero];
label.text = text;
[label sizeToFit];
return label;
}
- (void)addCenteredView:(UIView*)view toParentView:(UIView*)parentView {
view.center = [parentView convertPoint:parentView.center
fromView:parentView.superview];
[parentView addSubview:view];
}
@end

15
examples/ios/app/main.m Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2019 The Chromium 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 <UIKit/UIKit.h>
#import "app/AppDelegate.h"
int main(int argc, char** argv) {
NSString* appDelegateClassName;
@autoreleasepool {
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

104
examples/ios/build/BUILD.gn Normal file
View File

@ -0,0 +1,104 @@
# Copyright 2019 The Chromium 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/config/ios/deployment_target.gni")
config("compiler") {
configs = [
":include_dirs",
":cpp_standard",
":objc_use_arc",
":objc_abi_version",
]
cflags = [ "-g" ]
swiftflags = [ "-g" ]
}
config("shared_binary") {
if (current_os == "ios" || current_os == "mac") {
configs = [
":rpath_config",
":swift_libdir",
]
}
}
config("objc_abi_version") {
cflags_objc = [ "-fobjc-abi-version=2" ]
cflags_objcc = cflags_objc
ldflags = [
"-Xlinker",
"-objc_abi_version",
"-Xlinker",
"2",
]
}
config("include_dirs") {
include_dirs = [
"//",
root_gen_dir,
]
}
config("objc_use_arc") {
cflags_objc = [
"-fobjc-arc",
"-fobjc-weak",
]
cflags_objcc = cflags_objc
}
config("cpp_standard") {
cflags_c = [ "--std=c11" ]
cflags_cc = [
"--std=c++17",
"--stdlib=libc++",
]
ldflags = [ "--stdlib=libc++" ]
}
if (current_os == "ios" || current_os == "mac") {
config("rpath_config") {
ldflags = [
"-Xlinker",
"-rpath",
"-Xlinker",
"@executable_path/Frameworks",
"-Xlinker",
"-rpath",
"-Xlinker",
"@loader_path/Frameworks",
]
}
_sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py",
[
"--target-cpu",
current_cpu,
"--target-environment",
target_environment,
"--deployment-target",
ios_deployment_target,
],
"json")
_lib_swift_dir = "${_sdk_info.toolchain_path}/usr/lib/swift"
if (current_os == "ios") {
if (_sdk_info.is_simulator) {
_system_lib_swift_dir = "$_lib_swift_dir/iphonesimulator"
} else {
_system_lib_swift_dir = "$_lib_swift_dir/iphoneos"
}
} else {
_system_lib_swift_dir = "$_lib_swift_dir/macosx"
}
config("swift_libdir") {
lib_dirs = [
"${_sdk_info.sdk_path}/usr/lib/swift",
_system_lib_swift_dir,
]
}
}

View File

@ -0,0 +1,53 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
if (target_os == "") {
target_os = "ios"
}
if (target_cpu == "") {
target_cpu = host_cpu
}
if (current_cpu == "") {
current_cpu = target_cpu
}
if (current_os == "") {
current_os = target_os
}
declare_args() {
# Control which platform the build is targeting. Valid values are
# "simulator" or "device".
target_environment = "simulator"
}
assert(
target_environment == "simulator" || target_environment == "device",
"Only supported values for target_environment are 'simulator' and 'device'")
# All binary targets will get this list of configs by default.
_shared_binary_target_configs = [ "//build:compiler" ]
# Apply that default list to the binary target types.
set_defaults("executable") {
configs = _shared_binary_target_configs
configs += [ "//build:shared_binary" ]
}
set_defaults("static_library") {
configs = _shared_binary_target_configs
}
set_defaults("shared_library") {
configs = _shared_binary_target_configs
configs += [ "//build:shared_binary" ]
}
set_defaults("source_set") {
configs = _shared_binary_target_configs
}
set_default_toolchain("//build/toolchain/$target_os:clang_$target_cpu")
if (target_os == "ios") {
host_toolchain = "//build/toolchain/$host_os:clang_$host_cpu"
} else {
host_toolchain = default_toolchain
}

View File

@ -0,0 +1,24 @@
# Copyright 2019 The Chromium 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/config/ios/sdk_info.gni")
import("//build/config/ios/templates/merge_plist.gni")
merge_plist("compiler_plist") {
substitutions = {
COMPILER_NAME = sdk_info.compiler
MACOS_BUILD = sdk_info.macos_build
PLATFORM_BUILD = sdk_info.sdk_build
PLATFORM_DISPLAY_NAME = sdk_info.platform_name
PLATFORM_NAME = sdk_info.platform
PLATFORM_VERSION = sdk_info.sdk_version
SDK_BUILD = sdk_info.sdk_build
SDK_NAME = sdk_info.sdk
XCODE_BUILD = sdk_info.xcode_build
XCODE_VERSION = sdk_info.xcode_version
}
output = "$target_out_dir/compiler_plist/Info.plist"
plists = [ "//build/config/ios/resources/compiler-Info.plist" ]
}

View File

@ -0,0 +1,8 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
declare_args() {
# Default bundle identifier prefix.
default_bundle_identifier_prefix = "com.google"
}

View File

@ -0,0 +1,9 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
declare_args() {
# Maximum deployment target. Automatically detected by sdk_info.py but
# needs to be specified to a version < 11.0 if targetting 32-bit archs.
ios_deployment_target = "13.0"
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>DTSDKName</key>
<string>$(SDK_NAME)</string>
<key>DTXcode</key>
<string>$(XCODE_VERSION)</string>
<key>DTSDKBuild</key>
<string>$(SDK_BUILD)</string>
<key>BuildMachineOSBuild</key>
<string>$(MACOS_BUILD)</string>
<key>DTPlatformName</key>
<string>$(PLATFORM_NAME)</string>
<key>DTCompiler</key>
<string>$(COMPILER_NAME)</string>
<key>DTPlatformVersion</key>
<string>$(PLATFORM_VERSION)</string>
<key>DTXcodeBuild</key>
<string>$(XCODE_BUILD)</string>
<key>DTPlatformBuild</key>
<string>$(PLATFORM_BUILD)</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>$(PLATFORM_DISPLAY_NAME)</string>
</array>
<key>UIDeviceFamily</key>
<array>
<string>1</string>
<string>2</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,53 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Compiles a .storyboard file.
"""
import argparse
import os
import subprocess
import sys
def CompileStoryboard(storyboard, out, ios_deployment_target):
"""Compiles |storyboard| storyboard to |out| for |ios_deployment_target|."""
subprocess.check_call([
'ibtool', '--target-device', 'iphone', '--target-device', 'ipad',
'--auto-activate-custom-fonts', '--minimum-deployment-target',
ios_deployment_target, '--compilation-directory', out,
storyboard,
])
def ParseArgs(argv):
"""Parses command line arguments."""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
'input',
help='path to the .storyboard file to compile')
parser.add_argument(
'-o', '--output', required=True,
help='path to the result')
parser.add_argument(
'-t', '--minimum-deployment-target', required=True,
help='iOS deployment target')
return parser.parse_args(argv)
def main(argv):
args = ParseArgs(argv)
CompileStoryboard(
os.path.abspath(args.input),
os.path.dirname(os.path.abspath(args.output)),
args.minimum_deployment_target)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,119 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Finds the team identifier to use for code signing bundle given its
bundle identifier.
"""
import argparse
import fnmatch
import glob
import json
import os
import plistlib
import subprocess
import sys
class ProvisioningProfile(object):
def __init__(self, mobileprovision_path):
self._path = mobileprovision_path
self._data = plistlib.loads(
subprocess.check_output(
['security', 'cms', '-D', '-i', mobileprovision_path]))
@property
def application_identifier_pattern(self):
return self._data.get('Entitlements', {}).get('application-identifier', '')
@property
def app_identifier_prefix(self):
return self._data.get('ApplicationIdentifierPrefix', [''])[0]
def ValidToSignBundle(self, bundle_identifier):
"""Returns whether the provisioning profile can sign |bundle_identifier|."""
return fnmatch.fnmatch(
self.app_identifier_prefix + '.' + bundle_identifier,
self.application_identifier_pattern)
def GetProvisioningProfilesDir():
"""Returns the location of the locally installed provisioning profiles."""
return os.path.join(
os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles')
def ListProvisioningProfiles():
"""Returns a list of all installed provisioning profiles."""
return glob.glob(
os.path.join(GetProvisioningProfilesDir(), '*.mobileprovision'))
def LoadProvisioningProfile(mobileprovision_path):
"""Loads the Apple Property List embedded in |mobileprovision_path|."""
return ProvisioningProfile(mobileprovision_path)
def ListValidProvisioningProfiles(bundle_identifier):
"""Returns a list of provisioning profile valid for |bundle_identifier|."""
result = []
for mobileprovision_path in ListProvisioningProfiles():
mobileprovision = LoadProvisioningProfile(mobileprovision_path)
if mobileprovision.ValidToSignBundle(bundle_identifier):
result.append(mobileprovision)
return result
def FindProvisioningProfile(bundle_identifier):
"""Returns the path to the provisioning profile for |bundle_identifier|."""
return max(
ListValidProvisioningProfiles(bundle_identifier),
key=lambda p: len(p.application_identifier_pattern))
def GenerateSubsitutions(bundle_identifier, mobileprovision):
if mobileprovision:
app_identifier_prefix = mobileprovision.app_identifier_prefix + '.'
else:
app_identifier_prefix = '*.'
return {
'CFBundleIdentifier': bundle_identifier,
'AppIdentifierPrefix': app_identifier_prefix
}
def ParseArgs(argv):
"""Parses command line arguments."""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
'-b', '--bundle-identifier', required=True,
help='bundle identifier for the application')
parser.add_argument(
'-o', '--output', default='-',
help='path to the result; - means stdout')
return parser.parse_args(argv)
def main(argv):
args = ParseArgs(argv)
mobileprovision = FindProvisioningProfile(args.bundle_identifier)
substitutions = GenerateSubsitutions(args.bundle_identifier, mobileprovision)
if args.output == '-':
sys.stdout.write(json.dumps(substitutions))
else:
with open(args.output, 'w') as output:
output.write(json.dumps(substitutions))
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,54 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Generates an umbrella header file that #import all public header of a
binary framework.
"""
import argparse
import os
import sys
def GenerateImport(header):
"""Returns a string for importing |header|."""
return '#import "%s"\n' % os.path.basename(header)
def GenerateUmbrellaHeader(headers):
"""Returns a string with the content of the umbrella header."""
return ''.join([ GenerateImport(header) for header in headers ])
def ParseArgs(argv):
"""Parses command line arguments."""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
'headers', nargs='+',
help='path to the public heeaders')
parser.add_argument(
'-o', '--output', default='-',
help='path of the output file to create; - means stdout')
return parser.parse_args(argv)
def main(argv):
args = ParseArgs(argv)
content = GenerateUmbrellaHeader(args.headers)
if args.output == '-':
sys.stdout.write(content)
else:
with open(args.output, 'w') as output:
output.write(content)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,138 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Merges multiple Apple Property List files (.plist) and perform variables
substitutions $(VARIABLE) in the Property List string values.
"""
import argparse
import json
import re
import subprocess
import sys
# Pattern representing a variable to substitue in a string value.
VARIABLE_PATTERN = re.compile(r'\$\(([^)]*)\)')
def GetCommandOutput(command):
"""Returns the output of `command` as a string."""
return subprocess.check_output(command, encoding='utf-8')
def LoadPlist(plist_path):
"""Loads Apple Property List file at |plist_path|."""
return json.loads(
GetCommandOutput(['plutil', '-convert', 'json', '-o', '-', plist_path]))
def SavePlist(plist_path, content, format):
"""Saves |content| as Apple Property List in |format| at |plist_path|."""
proc = subprocess.Popen(
['plutil', '-convert', format, '-o', plist_path, '-'],
stdin=subprocess.PIPE)
output, _ = proc.communicate(json.dumps(content).encode('utf-8'))
if proc.returncode:
raise subprocess.CalledProcessError(
proc.returncode,
['plutil', '-convert', format, '-o', plist_path, '-'],
output)
def MergeObjects(obj1, obj2):
"""Merges two objects (either dictionary, list, string or numbers)."""
if type(obj1) != type(obj2):
return obj2
if isinstance(obj2, dict):
result = dict(obj1)
for key in obj2:
value1 = obj1.get(key, None)
value2 = obj2.get(key, None)
result[key] = MergeObjects(value1, value2)
return result
if isinstance(obj2, list):
return obj1 + obj2
return obj2
def MergePlists(plist_paths):
"""Loads and merges all Apple Property List files at |plist_paths|."""
plist = {}
for plist_path in plist_paths:
plist = MergeObjects(plist, LoadPlist(plist_path))
return plist
def PerformSubstitutions(plist, substitutions):
"""Performs variables substitutions in |plist| given by |substitutions|."""
if isinstance(plist, dict):
result = dict(plist)
for key in plist:
result[key] = PerformSubstitutions(plist[key], substitutions)
return result
if isinstance(plist, list):
return [ PerformSubstitutions(item, substitutions) for item in plist ]
if isinstance(plist, str):
result = plist
while True:
match = VARIABLE_PATTERN.search(result)
if not match:
break
extent = match.span()
expand = substitutions[match.group(1)]
result = result[:extent[0]] + expand + result[extent[1]:]
return result
return plist
def PerformSubstitutionsFrom(plist, substitutions_path):
"""Performs variable substitutions in |plist| from |substitutions_path|."""
with open(substitutions_path) as substitutions_file:
return PerformSubstitutions(plist, json.load(substitutions_file))
def ParseArgs(argv):
"""Parses command line arguments."""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
'-s', '--substitutions',
help='path to a JSON file containing variable substitutions')
parser.add_argument(
'-f', '--format', default='json', choices=('json', 'binary1', 'xml1'),
help='format of the generated file')
parser.add_argument(
'-o', '--output', default='-',
help='path to the result; - means stdout')
parser.add_argument(
'inputs', nargs='+',
help='path of the input files to merge')
return parser.parse_args(argv)
def main(argv):
args = ParseArgs(argv)
data = MergePlists(args.inputs)
if args.substitutions:
data = PerformSubstitutionsFrom(
data, args.substitutions)
SavePlist(args.output, data, args.format)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,159 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Collects information about the SDK and return them as JSON file."""
import argparse
import json
import os
import re
import subprocess
import sys
# Patterns used to extract the Xcode version and build version.
XCODE_VERSION_PATTERN = re.compile(r'Xcode (\d+)\.(\d+)')
XCODE_BUILD_PATTERN = re.compile(r'Build version (.*)')
def GetCommandOutput(command):
"""Returns the output of `command` as a string."""
return subprocess.check_output(command, encoding='utf-8')
def GetAppleCpuName(target_cpu):
"""Returns the name of the |target_cpu| using Apple's convention."""
return {
'x64': 'x86_64',
'arm': 'armv7',
'x86': 'i386'
}.get(target_cpu, target_cpu)
def GetPlatform(target_environment):
"""Returns the platform for |target_environment|."""
return {
'simulator': 'iphonesimulator',
'device': 'iphoneos'
}[target_environment]
def GetPlaformDisplayName(target_environment):
"""Returns the platform display name for |target_environment|."""
return {
'simulator': 'iPhoneSimulator',
'device': 'iPhoneOS'
}[target_environment]
def ExtractOSVersion():
"""Extract the version of macOS of the current machine."""
return GetCommandOutput(['sw_vers', '-buildVersion']).strip()
def ExtractXcodeInfo():
"""Extract Xcode version and build version."""
version, build = None, None
for line in GetCommandOutput(['xcodebuild', '-version']).splitlines():
match = XCODE_VERSION_PATTERN.search(line)
if match:
major, minor = match.group(1), match.group(2)
version = major.rjust(2, '0') + minor.ljust(2, '0')
continue
match = XCODE_BUILD_PATTERN.search(line)
if match:
build = match.group(1)
continue
assert version is not None and build is not None
return version, build
def ExtractSDKInfo(info, sdk):
"""Extract information about the SDK."""
return GetCommandOutput(['xcrun', '--sdk', sdk, '--show-sdk-' + info]).strip()
def GetDeveloperDir():
"""Returns the developer dir."""
return GetCommandOutput(['xcode-select', '-print-path']).strip()
def GetSDKInfoForCpu(target_cpu, environment, sdk_version, deployment_target):
"""Returns a dictionary with information about the SDK."""
platform = GetPlatform(environment)
sdk_version = sdk_version or ExtractSDKInfo('version', platform)
deployment_target = deployment_target or sdk_version
target = target_cpu + '-apple-ios' + deployment_target
if environment == 'simulator':
target = target + '-simulator'
xcode_version, xcode_build = ExtractXcodeInfo()
effective_sdk = platform + sdk_version
sdk_info = {}
sdk_info['compiler'] = 'com.apple.compilers.llvm.clang.1_0'
sdk_info['is_simulator'] = environment == 'simulator'
sdk_info['macos_build'] = ExtractOSVersion()
sdk_info['platform'] = platform
sdk_info['platform_name'] = GetPlaformDisplayName(environment)
sdk_info['sdk'] = effective_sdk
sdk_info['sdk_build'] = ExtractSDKInfo('build-version', effective_sdk)
sdk_info['sdk_path'] = ExtractSDKInfo('path', effective_sdk)
sdk_info['toolchain_path'] = os.path.join(
GetDeveloperDir(), 'Toolchains/XcodeDefault.xctoolchain')
sdk_info['sdk_version'] = sdk_version
sdk_info['target'] = target
sdk_info['xcode_build'] = xcode_build
sdk_info['xcode_version'] = xcode_version
return sdk_info
def ParseArgs(argv):
"""Parses command line arguments."""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument(
'-t', '--target-cpu', default='x64',
choices=('x86', 'x64', 'arm', 'arm64'),
help='target cpu')
parser.add_argument(
'-e',
'--target-environment',
default='simulator',
choices=('simulator', 'device'),
help='target environment')
parser.add_argument(
'-s', '--sdk-version',
help='version of the sdk')
parser.add_argument(
'-d', '--deployment-target',
help='iOS deployment target')
parser.add_argument(
'-o', '--output', default='-',
help='path of the output file to create; - means stdout')
return parser.parse_args(argv)
def main(argv):
args = ParseArgs(argv)
sdk_info = GetSDKInfoForCpu(
GetAppleCpuName(args.target_cpu), args.target_environment,
args.sdk_version, args.deployment_target)
if args.output == '-':
sys.stdout.write(json.dumps(sdk_info))
else:
with open(args.output, 'w') as output:
output.write(json.dumps(sdk_info))
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,16 @@
# Copyright 2019 The Chromium 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/config/ios/deployment_target.gni")
sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py",
[
"--target-cpu",
current_cpu,
"--target-environment",
target_environment,
"--deployment-target",
ios_deployment_target,
],
"json")

View File

@ -0,0 +1,147 @@
# Copyright 2019 The Chromium 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/config/ios/bundle_identifier_prefix.gni")
import("//build/config/ios/sdk_info.gni")
import("//build/config/ios/templates/ios_binary_bundle.gni")
import("//build/config/ios/templates/merge_plist.gni")
# Template to generate an app bundle.
#
# All the other parameters are forwarded to a shared_library target that will
# generate the bundle binary. In general, you want to pass at least "sources"
# or "deps" to have some binary objects included in your shared library.
#
# Arguments
#
# - info_plist (optional)
#
# path to additional Info.plist to merge into the final bundle Info.plist
#
# - bundle_identifier_prefix (optional)
#
# prefix for the bundle identifier (the full identifier will be defined
# to $bundle_identifier_prefix.$output_name); if unset will defaults to
# default_bundle_identifier_prefix
#
# - output_name (optional)
#
# name of the bundle without the extension; defaults to $target_name
#
template("ios_app_bundle") {
_output_name = target_name
if (defined(invoker.output_name)) {
_output_name = invoker.output_name
}
_bundle_identifier_prefix = default_bundle_identifier_prefix
if (defined(invoker.bundle_identifier_prefix)) {
_bundle_identifier_prefix = invoker.bundle_identifier_prefix
}
_bundle_identifier = "$_bundle_identifier_prefix.$_output_name"
_app_prefix_target = target_name + "_app_prefix"
_app_prefix_output = "$target_out_dir/$_app_prefix_target/app_prefix.json"
action(_app_prefix_target) {
script = "//build/config/ios/scripts/find_app_identifier_prefix.py"
sources = []
outputs = [ _app_prefix_output ]
args = [
"-b=" + _bundle_identifier,
"-o=" + rebase_path(_app_prefix_output, root_build_dir),
]
}
if (sdk_info.is_simulator) {
_simu_xcent_target = target_name + "_simu_xcent"
_simu_xcent_output =
"$target_out_dir/$_simu_xcent_target/" + "Entitlements-Simulated.plist"
merge_plist(_simu_xcent_target) {
format = "xml1"
output = _simu_xcent_output
plists = [ "//build/config/ios/resources/Entitlements-Simulated.plist" ]
substitutions_json = _app_prefix_output
deps = [ ":$_app_prefix_target" ]
}
}
_executable_target = target_name + "_executable"
_executable_bundle = target_name + "_executable_bundle"
executable(_executable_target) {
forward_variables_from(invoker,
"*",
[
"bundle_extension",
"bundle_identifier_prefix",
"bundle_type",
"display_name",
"info_plist",
"output_name",
"public_headers",
])
output_extension = ""
output_name = _output_name
output_prefix_override = true
output_dir = "$target_out_dir/$_executable_target"
if (sdk_info.is_simulator) {
if (!defined(deps)) {
deps = []
}
if (!defined(inputs)) {
inputs = []
}
if (!defined(ldflags)) {
ldflags = []
}
deps += [ ":$_simu_xcent_target" ]
inputs += [ _simu_xcent_output ]
ldflags += [
"-Xlinker",
"-sectcreate",
"-Xlinker",
"__TEXT",
"-Xlinker",
"__entitlements",
"-Xlinker",
rebase_path(_simu_xcent_output, root_build_dir),
]
}
}
bundle_data(_executable_bundle) {
public_deps = [ ":$_executable_target" ]
sources = [ "$target_out_dir/$_executable_target/$_output_name" ]
outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ]
}
ios_binary_bundle(target_name) {
forward_variables_from(invoker,
"*",
[
"bundle_extension",
"bundle_identifier_prefix",
"bundle_type",
"deps",
"output_name",
"public_deps",
"public_headers",
])
output_name = _output_name
product_type = "com.apple.product-type.application"
bundle_identifier = _bundle_identifier
bundle_extension = "app"
bundle_type = "AAPL"
public_deps = [ ":$_executable_bundle" ]
}
}

View File

@ -0,0 +1,127 @@
# Copyright 2019 The Chromium 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/config/ios/templates/merge_plist.gni")
# Template to create an Apple bundle containing a binary file (e.g. .app or
# .framework bundle).
#
# Arguments
#
# - bundle_extension
#
# extension of the bundle (e.g. "app", "framework", ...); must not
# include the dot preceding the extension
#
# - bundle_type
#
# four letter code corresponding to the bundle type ("FMWK", "AAPL",
# ...); used to fill the "Bundle OS Type code" value in the generated
# Info.plist for the bundle
#
# - bundle_identitier
#
# bundle identitifier
#
# - product_type
#
# type of the generated bundle (used for Xcode project)
#
# - output_name (optional)
#
# name of the bundle without the extension; the bundle binary (i.e.
# the application or the library) must have the same name; defaults
# to $target_name
#
# - display_name (optional)
#
# display name of the bundle (e.g. the name that is displayed to the
# user); defaults to $output_name
#
# - info_plist (optional)
#
# path to additional Info.plist to merge into the final bundle Info.plist
#
template("ios_binary_bundle") {
assert(
defined(invoker.bundle_extension),
"bundle_extension must be defined for ios_binary_bundle ($target_name)")
assert(
defined(invoker.bundle_identifier),
"bundle_identifier must be defined for ios_binary_bundle ($target_name)")
assert(defined(invoker.bundle_type),
"bundle_type must be defined for ios_binary_bundle ($target_name)")
assert(defined(invoker.product_type),
"product_type must be defined for ios_binary_bundle ($target_name)")
_output_name = target_name
if (defined(invoker.output_name)) {
_output_name = invoker.output_name
}
_display_name = _output_name
if (defined(invoker.display_name)) {
_display_name = invoker.display_name
}
_plist_target = target_name + "_plist"
_plist_bundle = target_name + "_plist_bundle"
merge_plist(_plist_target) {
substitutions = {
CURRENT_PROJECT_VERSION = "1"
DEVELOPMENT_LANGUAGE = "en"
EXECUTABLE_NAME = "$_output_name"
PRODUCT_BUNDLE_IDENTIFIER = invoker.bundle_identifier
PRODUCT_BUNDLE_PACKAGE_TYPE = invoker.bundle_type
PRODUCT_NAME = "$_display_name"
}
format = "binary1"
output = "$target_out_dir/$_plist_target/Info.plist"
plists = [
get_label_info("//build/config/ios:compiler_plist", "target_out_dir") +
"/compiler_plist/Info.plist",
"//build/config/ios/resources/Info.plist",
]
if (defined(invoker.info_plist)) {
plists += [ invoker.info_plist ]
}
deps = [ "//build/config/ios:compiler_plist" ]
}
bundle_data(_plist_bundle) {
public_deps = [ ":$_plist_target" ]
sources = [ "$target_out_dir/$_plist_target/Info.plist" ]
outputs = [ "{{bundle_contents_dir}}/Info.plist" ]
}
create_bundle(target_name) {
forward_variables_from(invoker,
"*",
[
"display_name",
"output_name",
"bundle_extension",
"bundle_type",
])
if (!defined(public_deps)) {
public_deps = []
}
public_deps += [ ":$_plist_bundle" ]
bundle_root_dir = "$root_out_dir/$_output_name.${invoker.bundle_extension}"
bundle_contents_dir = bundle_root_dir
bundle_executable_dir = bundle_contents_dir
bundle_resources_dir = bundle_contents_dir
xcode_extra_attributes = {
CODE_SIGN_IDENTITY = ""
CODE_SIGNING_REQUIRED = "NO"
CODE_SIGNING_ALLOWED = "NO"
}
}
}

View File

@ -0,0 +1,152 @@
# Copyright 2019 The Chromium 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/config/ios/bundle_identifier_prefix.gni")
import("//build/config/ios/templates/ios_binary_bundle.gni")
# Template to generate a framework bundle.
#
# All the other parameters are forwarded to a shared_library target that will
# generate the bundle binary. In general, you want to pass at least "sources"
# or "deps" to have some binary objects included in your shared library.
#
# Arguments
#
# - info_plist (optional)
#
# path to additional Info.plist to merge into the final bundle Info.plist
#
# - bundle_identifier_prefix (optional)
#
# prefix for the bundle identifier (the full identifier will be defined
# to $bundle_identifier_prefix.$output_name); if unset will defaults to
# default_bundle_identifier_prefix
#
# - output_name (optional)
#
# name of the bundle without the extension; defaults to $target_name
#
# - public_headers (optional)
#
# list of public headers files to copy into the framework bundle; this
# does not generate an umbrella header; an umbrella header named after
# the framework bundle will be created
#
template("ios_framework_bundle") {
_output_name = target_name
if (defined(invoker.output_name)) {
_output_name = invoker.output_name
}
_dylib_target = target_name + "_dylib"
_dylib_bundle = target_name + "_dylib_bundle"
_bundle_identifier_prefix = default_bundle_identifier_prefix
if (defined(invoker.bundle_identifier_prefix)) {
_bundle_identifier_prefix = invoker.bundle_identifier_prefix
}
_bundle_identifier = "$_bundle_identifier_prefix.$_output_name"
shared_library(_dylib_target) {
forward_variables_from(invoker,
"*",
[
"bundle_extension",
"bundle_identifier_prefix",
"bundle_type",
"display_name",
"info_plist",
"output_name",
"public_headers",
])
output_extension = ""
output_name = _output_name
output_prefix_override = true
output_dir = "$target_out_dir/$_dylib_target"
if (!defined(ldflags)) {
ldflags = []
}
ldflags += [
"-Xlinker",
"-install_name",
"-Xlinker",
"@rpath/$_output_name.framework/$_output_name",
]
}
bundle_data(_dylib_bundle) {
public_deps = [ ":$_dylib_target" ]
sources = [ "$target_out_dir/$_dylib_target/$_output_name" ]
outputs = [ "{{bundle_executable_dir}}/{{source_file_part}}" ]
}
if (defined(invoker.public_headers)) {
_umbrella_target = target_name + "_umbrella"
_umbrella_output = "$target_out_dir/$_umbrella_target/$_output_name.h"
action(_umbrella_target) {
script = "//build/config/ios/scripts/generate_umbrella_header.py"
sources = []
outputs = [ _umbrella_output ]
args = [ "-o=" + rebase_path(_umbrella_output, root_build_dir) ] +
rebase_path(invoker.public_headers, root_build_dir)
}
_headers_bundle = target_name + "_headers_bundle"
bundle_data(_headers_bundle) {
sources = invoker.public_headers + [ _umbrella_output ]
outputs = [ "{{bundle_resources_dir}}/Headers/{{source_file_part}}" ]
public_deps = [ ":$_umbrella_target" ]
}
}
_config_name = target_name + "_config"
config(_config_name) {
framework_dirs = [ root_out_dir ]
frameworks = [ "$_output_name.framework" ]
}
ios_binary_bundle(target_name) {
forward_variables_from(invoker,
"*",
[
"bundle_extension",
"bundle_type",
"configs",
"deps",
"output_name",
"public_configs",
"public_deps",
"public_headers",
])
output_name = _output_name
product_type = "com.apple.product-type.framework"
bundle_identifier = _bundle_identifier
bundle_extension = "framework"
bundle_type = "FMWK"
public_deps = [ ":$_dylib_bundle" ]
if (defined(invoker.public_headers)) {
public_deps += [ ":$_headers_bundle" ]
}
public_configs = [ ":$_config_name" ]
}
_target_name = target_name
bundle_data("$target_name+bundle") {
public_deps = [ ":$_target_name" ]
sources = [ "$root_out_dir/$_output_name.framework" ]
outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ]
}
}

View File

@ -0,0 +1,85 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Template to merge multiple Apple Property List file into a single file.
#
# Arguments
#
# - output
#
# path of the file that will be generated (must be in a sub-directory
# of root_build_dir)
#
# - plists
#
# list of path to Apple Property List file to merge (the file may be
# in either "json", "binary1" or "xml1" format)
#
# - format (optional)
#
# format in which the file must be saved; must be one of "json",
# "binary1" or "xml1" (default to "json" if omitted)
#
# - substitutions (optional)
#
# a scope defining variable substitutions to perform when merging the
# Property List files (i.e. if scope define foo = "bar", occurences
# of $(foo) in any string in a property list will be replaced by
# bar)
#
template("merge_plist") {
assert(defined(invoker.output) && invoker.output != "",
"output must be defined for merge_plist ($target_name)")
assert(defined(invoker.plists) && invoker.plists != [],
"plists must be defined for merge_plist ($target_name)")
if (defined(invoker.substitutions)) {
assert(!defined(invoker.substitutions_json),
"cannot define both substitutions and substitutions_json")
_substitutions_json = "$target_out_dir/$target_name/substitutions.json"
write_file(_substitutions_json, invoker.substitutions, "json")
}
if (defined(invoker.substitutions_json)) {
_substitutions_json = invoker.substitutions_json
}
action(target_name) {
forward_variables_from(invoker,
"*",
[
"args",
"format",
"inputs",
"output",
"plists",
"script",
"sources",
"substitutions",
"substitutions_json",
])
script = "//build/config/ios/scripts/merge_plist.py"
sources = invoker.plists
outputs = [ invoker.output ]
_format = "json"
if (defined(invoker.format)) {
_format = invoker.format
}
args = [
"-f=" + _format,
"-o=" + rebase_path(invoker.output, root_build_dir),
]
if (defined(_substitutions_json)) {
inputs = [ _substitutions_json ]
args += [ "-s=" + rebase_path(_substitutions_json) ]
}
args += rebase_path(sources, root_build_dir)
}
}

View File

@ -0,0 +1,31 @@
# Copyright 2019 The Chromium 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/config/ios/deployment_target.gni")
template("storyboards") {
assert(defined(invoker.sources),
"sources must be defined for storyboard ($target_name)")
_compile_target = target_name + "_compile"
_compile_output =
"$target_out_dir/$_compile_target/{{source_name_part}}.storyboardc"
action_foreach(_compile_target) {
script = "//build/config/ios/scripts/compile_storyboard.py"
sources = invoker.sources
outputs = [ _compile_output ]
args = [
"{{source}}",
"-o=" + rebase_path(_compile_output, root_build_dir),
"--minimum-deployment-target=$ios_deployment_target",
]
}
bundle_data(target_name) {
public_deps = [ ":$_compile_target" ]
sources = get_target_outputs(":$_compile_target")
outputs = [ "{{bundle_root_dir}}/Base.lproj/{{source_file_part}}" ]
}
}

View File

@ -0,0 +1,183 @@
#!/usr/bin/python3
import argparse
import collections
import json
import os
import subprocess
import sys
import tempfile
class OrderedSet(collections.OrderedDict):
def add(self, value):
self[value] = True
def compile_module(module, sources, settings, extras, tmpdir):
output_file_map = {}
if settings.whole_module_optimization:
output_file_map[''] = {
'object': os.path.join(settings.object_dir, module + '.o'),
'dependencies': os.path.join(tmpdir, module + '.d'),
}
else:
for source in sources:
name, _ = os.path.splitext(os.path.basename(source))
output_file_map[source] = {
'object': os.path.join(settings.object_dir, name + '.o'),
'dependencies': os.path.join(tmpdir, name + '.d'),
}
for key in ('module_path', 'header_path', 'depfile'):
path = getattr(settings, key)
if os.path.exists(path):
os.unlink(path)
if key == 'module_path':
for ext in '.swiftdoc', '.swiftsourceinfo':
path = os.path.splitext(getattr(settings, key))[0] + ext
if os.path.exists(path):
os.unlink(path)
directory = os.path.dirname(path)
if not os.path.exists(directory):
os.makedirs(directory)
if not os.path.exists(settings.object_dir):
os.makedirs(settings.object_dir)
for key in output_file_map:
path = output_file_map[key]['object']
if os.path.exists(path):
os.unlink(path)
output_file_map_path = os.path.join(tmpdir, module + '.json')
with open(output_file_map_path, 'w') as output_file_map_file:
output_file_map_file.write(json.dumps(output_file_map))
output_file_map_file.flush()
extra_args = []
if settings.bridge_header:
extra_args.extend([
'-import-objc-header',
os.path.abspath(settings.bridge_header),
])
if settings.whole_module_optimization:
extra_args.append('-whole-module-optimization')
if settings.target:
extra_args.extend([
'-target',
settings.target,
])
if settings.sdk:
extra_args.extend([
'-sdk',
os.path.abspath(settings.sdk),
])
if settings.include_dirs:
for include_dir in settings.include_dirs:
extra_args.append('-I' + include_dir)
process = subprocess.Popen(
['swiftc',
'-parse-as-library',
'-module-name',
module,
'-emit-object',
'-emit-dependencies',
'-emit-module',
'-emit-module-path',
settings.module_path,
'-emit-objc-header',
'-emit-objc-header-path',
settings.header_path,
'-output-file-map',
output_file_map_path,
] + extra_args + extras + sources,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
stdout, stderr = process.communicate()
if process.returncode:
sys.stdout.write(stdout)
sys.stderr.write(stderr)
sys.exit(process.returncode)
depfile_content = collections.OrderedDict()
for key in output_file_map:
for line in open(output_file_map[key]['dependencies']):
output, inputs = line.split(' : ', 2)
_, ext = os.path.splitext(output)
if ext == '.o':
key = output
else:
key = os.path.splitext(settings.module_path)[0] + ext
if key not in depfile_content:
depfile_content[key] = OrderedSet()
for path in inputs.split():
depfile_content[key].add(path)
with open(settings.depfile, 'w') as depfile:
for key in depfile_content:
if not settings.depfile_filter or key in settings.depfile_filter:
inputs = depfile_content[key]
depfile.write('%s : %s\n' % (key, ' '.join(inputs)))
def main(args):
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument(
'--module-name',
help='name of the Swift module')
parser.add_argument(
'--include', '-I', action='append', dest='include_dirs',
help='add directory to header search path')
parser.add_argument(
'sources', nargs='+',
help='Swift source file to compile')
parser.add_argument(
'--whole-module-optimization', action='store_true',
help='enable whole module optimization')
parser.add_argument(
'--object-dir', '-o',
help='path to the generated object files directory')
parser.add_argument(
'--module-path', '-m',
help='path to the generated module file')
parser.add_argument(
'--header-path', '-h',
help='path to the generated header file')
parser.add_argument(
'--bridge-header', '-b',
help='path to the Objective-C bridge header')
parser.add_argument(
'--depfile', '-d',
help='path to the generated depfile')
parser.add_argument(
'--depfile-filter', action='append',
help='limit depfile to those files')
parser.add_argument(
'--target', action='store',
help='generate code for the given target <triple>')
parser.add_argument(
'--sdk', action='store',
help='compile against sdk')
parsed, extras = parser.parse_known_args(args)
with tempfile.TemporaryDirectory() as tmpdir:
compile_module(
parsed.module_name,
parsed.sources,
parsed,
extras,
tmpdir)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@ -0,0 +1,169 @@
# Copyright 2019 The Chromium 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/config/ios/deployment_target.gni")
declare_args() {
# Configure whether whole module optimization is enabled when compiling
# swift modules.
swift_whole_module_optimization = true
}
template("ios_toolchain") {
toolchain(target_name) {
assert(defined(invoker.toolchain_args),
"Toolchains must declare toolchain_args")
toolchain_args = {
forward_variables_from(invoker.toolchain_args, "*")
}
_sdk_info = exec_script("//build/config/ios/scripts/sdk_info.py",
[
"--target-cpu",
current_cpu,
"--target-environment",
target_environment,
"--deployment-target",
ios_deployment_target,
],
"json")
cc = "clang -target ${_sdk_info.target} -isysroot ${_sdk_info.sdk_path}"
cxx = "clang++ -target ${_sdk_info.target} -isysroot ${_sdk_info.sdk_path}"
swiftmodule_switch = "-Wl,-add_ast_path,"
tool("link") {
output = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
rspfile = output + ".rsp"
rspfile_content = "{{inputs_newline}}"
outputs = [ output ]
command = "$cxx {{ldflags}} -o $output -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}} {{swiftmodules}}"
description = "LINK {{output}}"
default_output_dir = "{{root_out_dir}}"
default_output_extension = ""
output_prefix = ""
}
tool("solink") {
dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
rspfile = dylib + ".rsp"
rspfile_content = "{{inputs_newline}}"
outputs = [ dylib ]
command = "$cxx -dynamiclib {{ldflags}} -o $dylib -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}} {{swiftmodules}}"
description = "SOLINK {{output}}"
default_output_dir = "{{root_out_dir}}"
default_output_extension = ".dylib"
output_prefix = "lib"
}
tool("cc") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CC {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("cxx") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CXX {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("objc") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "OBJC {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("objcxx") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "OBJCXX {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("stamp") {
command = "touch {{output}}"
description = "STAMP {{output}}"
}
tool("copy_bundle_data") {
command = "rm -rf {{output}} && cp -PR {{source}} {{output}}"
description = "COPY_BUNDLE_DATA {{output}}"
}
tool("swift") {
depfile = "{{target_out_dir}}/{{module_name}}.d"
depsformat = "gcc"
outputs = [
# The module needs to be the first output to ensure the
# depfile generate works correctly with ninja < 1.9.0.
"{{target_gen_dir}}/{{module_name}}.swiftmodule",
"{{target_gen_dir}}/{{module_name}}.h",
"{{target_gen_dir}}/{{module_name}}.swiftdoc",
"{{target_gen_dir}}/{{module_name}}.swiftsourceinfo",
]
if (swift_whole_module_optimization) {
_extra_flag = "--whole-module-optimization"
_object_dir = "{{target_out_dir}}"
outputs += [ "{{target_out_dir}}/{{module_name}}.o" ]
} else {
_extra_flag = ""
_object_dir = "{{target_out_dir}}/{{label_name}}"
partial_outputs =
[ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
_swiftc = rebase_path("//build/toolchain/apple/swiftc.py", root_build_dir)
command = "$_swiftc --target ${_sdk_info.target} --sdk ${_sdk_info.sdk_path} --module-name {{module_name}} --object-dir $_object_dir --module-path {{target_gen_dir}}/{{module_name}}.swiftmodule --header-path {{target_gen_dir}}/{{module_name}}.h --depfile {{target_out_dir}}/{{module_name}}.d --depfile-filter {{target_gen_dir}}/{{module_name}}.swiftmodule --bridge-header {{bridge_header}} $_extra_flag {{defines}} {{swiftflags}} {{include_dirs}} {{module_dirs}} {{inputs}}"
}
}
}
ios_toolchain("clang_x86") {
toolchain_args = {
current_cpu = "x86"
current_os = "ios"
}
}
ios_toolchain("clang_x64") {
toolchain_args = {
current_cpu = "x64"
current_os = "ios"
}
}
ios_toolchain("clang_arm") {
toolchain_args = {
current_cpu = "arm"
current_os = "ios"
}
}
ios_toolchain("clang_arm64") {
toolchain_args = {
current_cpu = "arm64"
current_os = "ios"
}
}

View File

@ -0,0 +1,119 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
template("mac_toolchain") {
toolchain(target_name) {
assert(defined(invoker.toolchain_args),
"Toolchains must declare toolchain_args")
toolchain_args = {
forward_variables_from(invoker.toolchain_args, "*")
}
cc = "clang"
cxx = "clang++"
tool("link") {
output = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
rspfile = output + ".rsp"
rspfile_content = "{{inputs_newline}}"
outputs = [ output ]
command = "$cxx {{ldflags}} -o $output -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}"
description = "LINK {{output}}"
default_output_dir = "{{root_out_dir}}"
default_output_extension = ""
output_prefix = ""
}
tool("solink") {
dylib = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
rspfile = dylib + ".rsp"
rspfile_content = "{{inputs_newline}}"
outputs = [ dylib ]
command = "$cxx -dynamiclib {{ldflags}} -o $dylib -Wl,-filelist,$rspfile {{libs}} {{solibs}} {{frameworks}}"
description = "SOLINK {{output}}"
default_output_dir = "{{root_out_dir}}"
default_output_extension = ".dylib"
output_prefix = "lib"
}
tool("cc") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CC {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("cxx") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CXX {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("objc") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cc -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "OBJC {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("objcxx") {
depfile = "{{output}}.d"
precompiled_header_type = "gcc"
command = "$cxx -MMD -MF $depfile {{defines}} {{include_dirs}} {{framework_dirs}} {{cflags}} {{cflags_objcc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "OBJCXX {{output}}"
outputs = [ "{{target_out_dir}}/{{label_name}}/{{source_name_part}}.o" ]
}
tool("stamp") {
command = "touch {{output}}"
description = "STAMP {{output}}"
}
tool("copy_bundle_data") {
command = "rm -rf {{output}} && cp -a {{source}} {{output}}"
description = "COPY_BUNDLE_DATA {{output}}"
}
}
}
mac_toolchain("clang_x86") {
toolchain_args = {
current_cpu = "x86"
current_os = "mac"
}
}
mac_toolchain("clang_x64") {
toolchain_args = {
current_cpu = "x64"
current_os = "mac"
}
}
mac_toolchain("clang_arm") {
toolchain_args = {
current_cpu = "arm"
current_os = "mac"
}
}
mac_toolchain("clang_arm64") {
toolchain_args = {
current_cpu = "arm64"
current_os = "mac"
}
}

View File

@ -0,0 +1,13 @@
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
if (current_toolchain == host_toolchain) {
executable("username") {
sources = [ "main.cc" ]
}
} else {
group("username") {
deps = [ ":username($host_toolchain)" ]
}
}

71
examples/ios/host/main.cc Normal file
View File

@ -0,0 +1,71 @@
// Copyright 2019 The Chromium 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 <stdlib.h>
#include <iostream>
#include <string>
namespace {
// Returns the current user username.
std::string Username() {
const char* username = getenv("USER");
return username ? std::string(username) : std::string();
}
// Writes |string| to |stream| while escaping all C escape sequences.
void EscapeString(std::ostream* stream, const std::string& string) {
for (char c : string) {
switch (c) {
case 0:
*stream << "\\0";
break;
case '\a':
*stream << "\\a";
break;
case '\b':
*stream << "\\b";
break;
case '\e':
*stream << "\\e";
break;
case '\f':
*stream << "\\f";
break;
case '\n':
*stream << "\\n";
break;
case '\r':
*stream << "\\r";
break;
case '\t':
*stream << "\\t";
break;
case '\v':
*stream << "\\v";
break;
case '\\':
*stream << "\\\\";
break;
case '\"':
*stream << "\\\"";
break;
default:
*stream << c;
break;
}
}
}
} // namespace
int main(int argc, char** argv) {
std::string username = Username();
std::cout << "{\"username\": \"";
EscapeString(&std::cout, username);
std::cout << "\"}" << std::endl;
return 0;
}

View File

@ -0,0 +1,18 @@
# Copyright 2019 The Chromium 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/config/ios/templates/ios_framework_bundle.gni")
ios_framework_bundle("hello_framework") {
output_name = "HelloShared"
sources = [
"hello_shared.h",
"hello_shared.m",
]
public_headers = [ "hello_shared.h" ]
defines = [ "HELLO_SHARED_IMPLEMENTATION" ]
frameworks = [ "Foundation.framework" ]
}

View File

@ -0,0 +1,13 @@
// Copyright 2019 The Chromium 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 <Foundation/Foundation.h>
@interface Greetings : NSObject
+ (NSString*)greet;
+ (NSString*)greetWithName:(NSString*)name;
@end

View File

@ -0,0 +1,22 @@
// Copyright 2019 The Chromium 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 "hello_shared.h"
@implementation Greetings
+ (NSString*)greet {
return [NSString stringWithFormat:@"Hello from %@!", [Greetings displayName]];
}
+ (NSString*)greetWithName:(NSString*)name {
return [NSString stringWithFormat:@"Hello, %@!", name];
}
+ (NSString*)displayName {
NSBundle* bundle = [NSBundle bundleForClass:[Greetings class]];
return [[bundle infoDictionary] objectForKey:@"CFBundleName"];
}
@end

View File

@ -0,0 +1 @@
buildconfig = "//BUILDCONFIG.gn"

View File

@ -0,0 +1,3 @@
group("default") {
deps = [ "//hello_world/src:hello_world" ]
}

View File

@ -0,0 +1,23 @@
if (target_os == "") {
target_os = host_os
}
if (target_cpu == "") {
target_cpu = host_cpu
}
if (current_cpu == "") {
current_cpu = target_cpu
}
if (current_os == "") {
current_os = target_os
}
_configs = [ "//build:rust_defaults" ]
set_defaults("executable") {
configs = _configs
}
set_defaults("rust_library") {
configs = _configs
}
set_default_toolchain("//build:rust")

View File

@ -0,0 +1,4 @@
This is an example directory structure that compiles a Rust hello world
example. It is intended to show how to set up a simple Rust GN build.
Don't miss the ".gn" file in this directory!

View File

@ -0,0 +1,47 @@
toolchain("rust") {
tool("rust_bin") {
depfile = "{{target_out_dir}}/{{crate_name}}.d"
outfile = "{{target_out_dir}}/{{crate_name}}"
command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
description = "RUST $outfile"
outputs = [ outfile ]
}
tool("rust_staticlib") {
depfile = "{{target_out_dir}}/{{crate_name}}.d"
outfile = "{{target_out_dir}}/{{crate_name}}.a"
command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
description = "RUST $outfile"
outputs = [ outfile ]
}
tool("rust_rlib") {
depfile = "{{target_out_dir}}/{{crate_name}}.d"
outfile = "{{target_out_dir}}/lib{{crate_name}}.rlib"
command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
description = "RUST $outfile"
outputs = [ outfile ]
}
tool("rust_cdylib") {
depfile = "{{target_out_dir}}/{{crate_name}}.d"
outfile = "{{target_out_dir}}/lib{{crate_name}}.so"
command = "rustc --crate-name {{crate_name}} {{source}} --crate-type {{crate_type}} --emit=dep-info=$depfile,link -Z dep-info-omit-d-target {{rustflags}} -o $outfile {{rustdeps}} {{externs}}"
description = "RUST $outfile"
outputs = [ outfile ]
}
tool("stamp") {
command = "touch {{output}}"
description = "STAMP {{output}}"
}
tool("copy") {
command = "cp -af {{source}} {{output}}"
description = "COPY {{source}} {{output}}"
}
}
config("rust_defaults") {
rustflags = [ "-Cdebuginfo=2" ]
}

View File

@ -0,0 +1,3 @@
rust_library("bar") {
crate_root = "lib.rs"
}

View File

@ -0,0 +1,23 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
#[derive(Debug)]
pub struct Foo {
s: &'static str,
i: &'static str
}
impl Foo {
pub fn new(s: &'static str) -> Foo {
Foo{s: s, i: "bar"}
}
}
pub fn answer() -> i32 {
42
}

View File

@ -0,0 +1,4 @@
rust_library("foo") {
sources = [ "lib.rs" ]
deps = [ "//hello_world/bar/src:bar" ]
}

View File

@ -0,0 +1,11 @@
#[derive(Debug)]
pub struct Foo {
s: &'static str,
i: &'static str
}
impl Foo {
pub fn new(s: &'static str) -> Foo {
Foo{s: s, i: "foo"}
}
}

View File

@ -0,0 +1,7 @@
executable("hello_world") {
sources = [ "main.rs" ]
deps = [ "//hello_world/foo/src:foo" ]
aliased_deps = {
baz = "//hello_world/foo/src:foo"
}
}

View File

@ -0,0 +1,5 @@
fn main() {
let f = baz::Foo::new("hello");
println!("Hello world!");
println!("I'm from a dependency: {:?}!", f);
}

View File

@ -0,0 +1,2 @@
# The location of the build configuration file.
buildconfig = "//build/BUILDCONFIG.gn"

View File

@ -0,0 +1,28 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
executable("hello") {
sources = [ "hello.cc" ]
deps = [
":hello_shared",
":hello_static",
]
}
shared_library("hello_shared") {
sources = [
"hello_shared.cc",
"hello_shared.h",
]
defines = [ "HELLO_SHARED_IMPLEMENTATION" ]
}
static_library("hello_static") {
sources = [
"hello_static.cc",
"hello_static.h",
]
}

View File

@ -0,0 +1,12 @@
# GN Simple Build Example
This is an example directory structure that compiles some simple targets using
gcc. It is intended to show how to set up a simple GN build. It is deliberately
simplistic so the structure is more clear, and doesn't support everything on
every platform.
It is recommended that you take this and add on as your requirements indicate.
You may also want to see the Chromium and Fuchsia build configurations for the
maximal functionality (the [root README](../../README.md) has links to these).
Don't miss the ".gn" file in this directory which may be hidden on your system!

View File

@ -0,0 +1,21 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
config("compiler_defaults") {
if (current_os == "linux") {
cflags = [
"-fPIC",
"-pthread",
]
}
}
config("executable_ldconfig") {
if (!is_mac) {
ldflags = [
"-Wl,-rpath=\$ORIGIN/",
"-Wl,-rpath-link=",
]
}
}

View File

@ -0,0 +1,41 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
if (target_os == "") {
target_os = host_os
}
if (target_cpu == "") {
target_cpu = host_cpu
}
if (current_cpu == "") {
current_cpu = target_cpu
}
if (current_os == "") {
current_os = target_os
}
is_linux = host_os == "linux" && current_os == "linux" && target_os == "linux"
is_mac = host_os == "mac" && current_os == "mac" && target_os == "mac"
# All binary targets will get this list of configs by default.
_shared_binary_target_configs = [ "//build:compiler_defaults" ]
# Apply that default list to the binary target types.
set_defaults("executable") {
configs = _shared_binary_target_configs
# Executables get this additional configuration.
configs += [ "//build:executable_ldconfig" ]
}
set_defaults("static_library") {
configs = _shared_binary_target_configs
}
set_defaults("shared_library") {
configs = _shared_binary_target_configs
}
set_defaults("source_set") {
configs = _shared_binary_target_configs
}
set_default_toolchain("//build/toolchain:gcc")

View File

@ -0,0 +1,88 @@
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
toolchain("gcc") {
tool("cc") {
depfile = "{{output}}.d"
command = "gcc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CC {{output}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
}
tool("cxx") {
depfile = "{{output}}.d"
command = "g++ -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
depsformat = "gcc"
description = "CXX {{output}}"
outputs =
[ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
}
tool("alink") {
command = "rm -f {{output}} && ar rcs {{output}} {{inputs}}"
description = "AR {{target_output_name}}{{output_extension}}"
outputs =
[ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
default_output_extension = ".a"
output_prefix = "lib"
}
tool("solink") {
soname = "{{target_output_name}}{{output_extension}}" # e.g. "libfoo.so".
sofile = "{{output_dir}}/$soname"
rspfile = soname + ".rsp"
if (is_mac) {
os_specific_option = "-install_name @executable_path/$sofile"
rspfile_content = "{{inputs}} {{solibs}} {{libs}}"
} else {
os_specific_option = "-Wl,-soname=$soname"
rspfile_content = "-Wl,--whole-archive {{inputs}} {{solibs}} -Wl,--no-whole-archive {{libs}}"
}
command = "g++ -shared {{ldflags}} -o $sofile $os_specific_option @$rspfile"
description = "SOLINK $soname"
# Use this for {{output_extension}} expansions unless a target manually
# overrides it (in which case {{output_extension}} will be what the target
# specifies).
default_output_extension = ".so"
# Use this for {{output_dir}} expansions unless a target manually overrides
# it (in which case {{output_dir}} will be what the target specifies).
default_output_dir = "{{root_out_dir}}"
outputs = [ sofile ]
link_output = sofile
depend_output = sofile
output_prefix = "lib"
}
tool("link") {
outfile = "{{target_output_name}}{{output_extension}}"
rspfile = "$outfile.rsp"
if (is_mac) {
command = "g++ {{ldflags}} -o $outfile @$rspfile {{solibs}} {{libs}}"
} else {
command = "g++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
}
description = "LINK $outfile"
default_output_dir = "{{root_out_dir}}"
rspfile_content = "{{inputs}}"
outputs = [ outfile ]
}
tool("stamp") {
command = "touch {{output}}"
description = "STAMP {{output}}"
}
tool("copy") {
command = "cp -af {{source}} {{output}}"
description = "COPY {{source}} {{output}}"
}
}

View File

@ -0,0 +1,13 @@
// Copyright 2014 The Chromium 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 <stdio.h>
#include "hello_shared.h"
#include "hello_static.h"
int main(int argc, char* argv[]) {
printf("%s, %s\n", GetStaticText(), GetSharedText());
return 0;
}

View File

@ -0,0 +1,9 @@
// Copyright 2014 The Chromium 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 "hello_shared.h"
const char* GetSharedText() {
return "world";
}

View File

@ -0,0 +1,32 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_GN_EXAMPLE_HELLO_SHARED_H_
#define TOOLS_GN_EXAMPLE_HELLO_SHARED_H_
#if defined(WIN32)
#if defined(HELLO_SHARED_IMPLEMENTATION)
#define HELLO_EXPORT __declspec(dllexport)
#define HELLO_EXPORT_PRIVATE __declspec(dllexport)
#else
#define HELLO_EXPORT __declspec(dllimport)
#define HELLO_EXPORT_PRIVATE __declspec(dllimport)
#endif // defined(HELLO_SHARED_IMPLEMENTATION)
#else
#if defined(HELLO_SHARED_IMPLEMENTATION)
#define HELLO_EXPORT __attribute__((visibility("default")))
#define HELLO_EXPORT_PRIVATE __attribute__((visibility("default")))
#else
#define HELLO_EXPORT
#define HELLO_EXPORT_PRIVATE
#endif // defined(HELLO_SHARED_IMPLEMENTATION)
#endif
HELLO_EXPORT const char* GetSharedText();
#endif // TOOLS_GN_EXAMPLE_HELLO_SHARED_H_

View File

@ -0,0 +1,9 @@
// Copyright 2014 The Chromium 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 "hello_static.h"
const char* GetStaticText() {
return "Hello";
}

View File

@ -0,0 +1,10 @@
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TOOLS_GN_EXAMPLE_HELLO_STATIC_H_
#define TOOLS_GN_EXAMPLE_HELLO_STATIC_H_
const char* GetStaticText();
#endif // TOOLS_GN_EXAMPLE_HELLO_STATIC_H_

View File

@ -0,0 +1,4 @@
# Tutorial directory
This directory isn't used by the simple build example by default. It's here to
be used for the example in the [quick start guide](../../../docs/quick_start.md).

View File

@ -0,0 +1,10 @@
// Copyright 2020 The Chromium 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 <stdio.h>
int main(int argc, char* argv[]) {
printf("Hello from the tutorial.\n");
return 0;
}

138
infra/README.recipes.md Normal file
View File

@ -0,0 +1,138 @@
<!--- AUTOGENERATED BY `./recipes.py test train` -->
# Repo documentation for [gn]()
## Table of Contents
**[Recipe Modules](#Recipe-Modules)**
* [macos_sdk](#recipe_modules-macos_sdk) (Python3 ✅) &mdash; The `macos_sdk` module provides safe functions to access a semi-hermetic XCode installation.
* [target](#recipe_modules-target) (Python3 ✅)
* [windows_sdk](#recipe_modules-windows_sdk) (Python3 ✅)
**[Recipes](#Recipes)**
* [gn](#recipes-gn) (Python3 ✅) &mdash; Recipe for building GN.
* [macos_sdk:examples/full](#recipes-macos_sdk_examples_full) (Python3 ✅)
* [target:examples/full](#recipes-target_examples_full) (Python3 ✅)
* [windows_sdk:examples/full](#recipes-windows_sdk_examples_full) (Python3 ✅)
## Recipe Modules
### *recipe_modules* / [macos\_sdk](/infra/recipe_modules/macos_sdk)
[DEPS](/infra/recipe_modules/macos_sdk/__init__.py#5): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
PYTHON_VERSION_COMPATIBILITY: PY3
The `macos_sdk` module provides safe functions to access a semi-hermetic
XCode installation.
Available only to Google-run bots.
#### **class [MacOSSDKApi](/infra/recipe_modules/macos_sdk/api.py#14)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
API for using OS X SDK distributed via CIPD.
&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/macos_sdk/api.py#30)(self):**
Sets up the XCode SDK environment.
This call is a no-op on non-Mac platforms.
This will deploy the helper tool and the XCode.app bundle at
`[START_DIR]/cache/macos_sdk`.
To avoid machines rebuilding these on every run, set up a named cache in
your cr-buildbucket.cfg file like:
caches: {
# Cache for mac_toolchain tool and XCode.app
name: "macos_sdk"
path: "macos_sdk"
}
If you have builders which e.g. use a non-current SDK, you can give them
a uniqely named cache:
caches: {
# Cache for N-1 version mac_toolchain tool and XCode.app
name: "macos_sdk_old"
path: "macos_sdk"
}
Usage:
with api.macos_sdk():
# sdk with mac build bits
Raises:
StepFailure or InfraFailure.
&emsp; **@property**<br>&mdash; **def [sdk\_dir](/infra/recipe_modules/macos_sdk/api.py#25)(self):**
### *recipe_modules* / [target](/infra/recipe_modules/target)
[DEPS](/infra/recipe_modules/target/__init__.py#5): [recipe\_engine/platform][recipe_engine/recipe_modules/platform]
PYTHON_VERSION_COMPATIBILITY: PY3
#### **class [TargetApi](/infra/recipe_modules/target/api.py#82)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
&emsp; **@property**<br>&mdash; **def [host](/infra/recipe_modules/target/api.py#87)(self):**
### *recipe_modules* / [windows\_sdk](/infra/recipe_modules/windows_sdk)
[DEPS](/infra/recipe_modules/windows_sdk/__init__.py#5): [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/step][recipe_engine/recipe_modules/step]
PYTHON_VERSION_COMPATIBILITY: PY3
#### **class [WindowsSDKApi](/infra/recipe_modules/windows_sdk/api.py#10)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
API for using Windows SDK distributed via CIPD.
&emsp; **@contextmanager**<br>&mdash; **def [\_\_call\_\_](/infra/recipe_modules/windows_sdk/api.py#19)(self):**
Setups the Windows SDK environment.
This call is a no-op on non-Windows platforms.
Raises:
StepFailure or InfraFailure.
## Recipes
### *recipes* / [gn](/infra/recipes/gn.py)
[DEPS](/infra/recipes/gn.py#8): [macos\_sdk](#recipe_modules-macos_sdk), [target](#recipe_modules-target), [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cas][recipe_engine/recipe_modules/cas], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
PYTHON_VERSION_COMPATIBILITY: PY3
Recipe for building GN.
&mdash; **def [RunSteps](/infra/recipes/gn.py#103)(api, repository):**
### *recipes* / [macos\_sdk:examples/full](/infra/recipe_modules/macos_sdk/examples/full.py)
[DEPS](/infra/recipe_modules/macos_sdk/examples/full.py#5): [macos\_sdk](#recipe_modules-macos_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
PYTHON_VERSION_COMPATIBILITY: PY3
&mdash; **def [RunSteps](/infra/recipe_modules/macos_sdk/examples/full.py#13)(api):**
### *recipes* / [target:examples/full](/infra/recipe_modules/target/examples/full.py)
[DEPS](/infra/recipe_modules/target/examples/full.py#5): [target](#recipe_modules-target), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
PYTHON_VERSION_COMPATIBILITY: PY3
&mdash; **def [RunSteps](/infra/recipe_modules/target/examples/full.py#13)(api):**
### *recipes* / [windows\_sdk:examples/full](/infra/recipe_modules/windows_sdk/examples/full.py)
[DEPS](/infra/recipe_modules/windows_sdk/examples/full.py#5): [windows\_sdk](#recipe_modules-windows_sdk), [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
PYTHON_VERSION_COMPATIBILITY: PY3
&mdash; **def [RunSteps](/infra/recipe_modules/windows_sdk/examples/full.py#13)(api):**
[recipe_engine/recipe_modules/buildbucket]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-buildbucket
[recipe_engine/recipe_modules/cas]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-cas
[recipe_engine/recipe_modules/cipd]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-cipd
[recipe_engine/recipe_modules/context]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-context
[recipe_engine/recipe_modules/file]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-file
[recipe_engine/recipe_modules/json]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-json
[recipe_engine/recipe_modules/path]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-path
[recipe_engine/recipe_modules/platform]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-platform
[recipe_engine/recipe_modules/properties]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-properties
[recipe_engine/recipe_modules/raw_io]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-raw_io
[recipe_engine/recipe_modules/step]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/README.recipes.md#recipe_modules-step
[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/ff5ce51ceaaa7a62275bb766831a3ce1e88de37d/recipe_engine/recipe_api.py#886

View File

@ -0,0 +1,46 @@
# Auto-generated by lucicfg.
# Do not modify manually.
#
# For the schema of this file, see Config message:
# https://luci-config.appspot.com/schemas/projects:commit-queue.cfg
submit_options {
max_burst: 4
burst_delay {
seconds: 480
}
}
config_groups {
name: "gn"
gerrit {
url: "https://gn-review.googlesource.com"
projects {
name: "gn"
ref_regexp: "refs/heads/main"
}
}
verifiers {
gerrit_cq_ability {
committer_list: "project-gn-committers"
dry_run_access_list: "project-gn-tryjob-access"
}
tryjob {
builders {
name: "gn/try/linux"
}
builders {
name: "gn/try/mac"
}
builders {
name: "gn/try/win"
}
retry_config {
single_quota: 1
global_quota: 2
failure_weight: 1
transient_failure_weight: 1
timeout_weight: 2
}
}
}
}

View File

@ -0,0 +1,154 @@
# Auto-generated by lucicfg.
# Do not modify manually.
#
# For the schema of this file, see BuildbucketCfg message:
# https://luci-config.appspot.com/schemas/projects:buildbucket.cfg
buckets {
name: "ci"
acls {
group: "all"
}
swarming {
builders {
name: "linux"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
dimensions: "os:Ubuntu-18.04"
dimensions: "pool:luci.flex.ci"
recipe {
name: "gn"
cipd_package: "infra/recipe_bundles/gn.googlesource.com/gn"
cipd_version: "refs/heads/main"
}
execution_timeout_secs: 3600
service_account: "gn-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
value: 100
}
}
builders {
name: "mac"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
dimensions: "os:Mac-10.15"
dimensions: "pool:luci.flex.ci"
recipe {
name: "gn"
cipd_package: "infra/recipe_bundles/gn.googlesource.com/gn"
cipd_version: "refs/heads/main"
}
execution_timeout_secs: 3600
caches {
name: "macos_sdk"
path: "macos_sdk"
}
service_account: "gn-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
value: 100
}
}
builders {
name: "win"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
dimensions: "os:Windows-10"
dimensions: "pool:luci.flex.ci"
recipe {
name: "gn"
cipd_package: "infra/recipe_bundles/gn.googlesource.com/gn"
cipd_version: "refs/heads/main"
}
execution_timeout_secs: 3600
caches {
name: "windows_sdk"
path: "windows_sdk"
}
service_account: "gn-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
value: 100
}
}
}
}
buckets {
name: "try"
acls {
group: "all"
}
acls {
role: SCHEDULER
group: "project-gn-tryjob-access"
}
acls {
role: SCHEDULER
group: "service-account-cq"
}
swarming {
builders {
name: "linux"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
dimensions: "os:Ubuntu-18.04"
dimensions: "pool:luci.flex.try"
recipe {
name: "gn"
cipd_package: "infra/recipe_bundles/gn.googlesource.com/gn"
cipd_version: "refs/heads/main"
}
execution_timeout_secs: 3600
service_account: "gn-try-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
value: 100
}
}
builders {
name: "mac"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
dimensions: "os:Mac-10.15"
dimensions: "pool:luci.flex.try"
recipe {
name: "gn"
cipd_package: "infra/recipe_bundles/gn.googlesource.com/gn"
cipd_version: "refs/heads/main"
}
execution_timeout_secs: 3600
caches {
name: "macos_sdk"
path: "macos_sdk"
}
service_account: "gn-try-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
value: 100
}
}
builders {
name: "win"
swarming_host: "chromium-swarm.appspot.com"
dimensions: "cpu:x86-64"
dimensions: "os:Windows-10"
dimensions: "pool:luci.flex.try"
recipe {
name: "gn"
cipd_package: "infra/recipe_bundles/gn.googlesource.com/gn"
cipd_version: "refs/heads/main"
}
execution_timeout_secs: 3600
caches {
name: "windows_sdk"
path: "windows_sdk"
}
service_account: "gn-try-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
value: 100
}
}
}
}

View File

@ -0,0 +1,9 @@
# Auto-generated by lucicfg.
# Do not modify manually.
#
# For the schema of this file, see ProjectConfig message:
# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg
reader_auth_groups: "all"
writer_auth_groups: "luci-logdog-chromium-writers"
archive_gs_bucket: "chromium-luci-logdog"

View File

@ -0,0 +1,27 @@
# Auto-generated by lucicfg.
# Do not modify manually.
#
# For the schema of this file, see Project message:
# https://luci-config.appspot.com/schemas/projects:luci-milo.cfg
consoles {
id: "gn"
name: "gn"
repo_url: "https://gn.googlesource.com/gn"
refs: "regexp:refs/heads/main"
manifest_name: "REVISION"
builders {
name: "buildbucket/luci.gn.ci/linux"
short_name: "linux"
}
builders {
name: "buildbucket/luci.gn.ci/mac"
short_name: "mac"
}
builders {
name: "buildbucket/luci.gn.ci/win"
short_name: "win"
}
favicon_url: "https://storage.googleapis.com/chrome-infra-public/logo/favicon.ico"
}
logo_url: "https://storage.googleapis.com/chrome-infra-public/logo/gn-logo.png"

Some files were not shown because too many files have changed in this diff Show More