Add support for building variants of app on iOS.

Chrome on iOS needs to build the same app (exact same binary)
with different application icons (i.e. with different compiled
asset catalogs).

Add a "variants" property to "ios_app_bundle" template that
contains a list of scope, each defining the variant "name" and
the list of additional "bundle_deps".

When the "variants" property is defined, the executable will
be linked once, but n application bundles will be generated in
$root_out_dir/variants/$variant_name and a copy of the first
variant will be created in $root_out_dir.

The hardlink.py script is used to copy the first variant (it
tries to use hardlink if possible, but fallback to copy if it
fails).

Bug: 764286
Change-Id: I0ea18e757c891ae884ba22c07fe20946715d1d0a
Reviewed-on: https://chromium-review.googlesource.com/677386
Commit-Queue: Sylvain Defresne <sdefresne@chromium.org>
Reviewed-by: Olivier Robin <olivierrobin@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#503710}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 7aac0a1de0b0c010efde682ad414770eb04eb4b0
This commit is contained in:
Sylvain Defresne 2017-09-22 10:43:52 +00:00 коммит произвёл Commit Bot
Родитель 2cf030dbbf
Коммит aaf141004b
2 изменённых файлов: 197 добавлений и 41 удалений

69
config/ios/hardlink.py Normal file
Просмотреть файл

@ -0,0 +1,69 @@
# Copyright 2017 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.
"""Recursively create hardlink to target named output."""
import argparse
import os
import shutil
def CreateHardlinkHelper(target, output):
"""Recursively create a hardlink named output pointing to target.
Args:
target: path to an existing file or directory
output: path to the newly created hardlink
This function assumes that output does not exists but that the parent
directory containing output does. If those conditions are false, then
the function will fails with an exception corresponding to an OS error.
"""
if os.path.islink(target):
os.symlink(os.readlink(target), output)
elif not os.path.isdir(target):
try:
os.link(target, output)
except:
shutil.copy(target, output)
else:
os.mkdir(output)
for name in os.listdir(target):
CreateHardlinkHelper(
os.path.join(target, name),
os.path.join(output, name))
def CreateHardlink(target, output):
"""Recursively create a hardlink named output pointing to target.
Args:
target: path to an existing file or directory
output: path to the newly created hardlink
If output already exists, it is first removed. In all cases, the
parent directory containing output is created.
"""
if os.path.exists(output):
shutil.rmtree(output)
parent_dir = os.path.dirname(os.path.abspath(output))
if not os.path.isdir(parent_dir):
os.makedirs(parent_dir)
CreateHardlinkHelper(target, output)
def Main():
parser = argparse.ArgumentParser()
parser.add_argument('target', help='path to the file or directory to link to')
parser.add_argument('output', help='name of the hardlink to create')
args = parser.parse_args()
CreateHardlink(args.target, args.output)
if __name__ == '__main__':
Main()

Просмотреть файл

@ -122,12 +122,16 @@ template("lipo_binary") {
# product_type
# string, product type for the generated Xcode project.
#
# bundle_gen_dir
# (optional) directory where the bundle is generated; must be below
# root_out_dir and defaults to root_out_dir if omitted.
#
# bundle_deps
# (optional) list of additional dependencies
# (optional) list of additional dependencies.
#
# bundle_deps_filter
# (optional) list of dependencies to filter (for more information
# see "gn help bundle_deps_filter")
# see "gn help bundle_deps_filter").
#
# bundle_extension
# string, extension of the bundle, used to generate bundle name.
@ -220,6 +224,11 @@ template("create_signed_bundle") {
"/$_bundle_binary_output"
}
_bundle_gen_dir = root_out_dir
if (defined(invoker.bundle_gen_dir)) {
_bundle_gen_dir = invoker.bundle_gen_dir
}
_bundle_extension = invoker.bundle_extension
if (!defined(invoker.entitlements_target)) {
@ -258,7 +267,7 @@ template("create_signed_bundle") {
"xcode_test_application_name",
])
bundle_root_dir = "$root_out_dir/$_output_name$_bundle_extension"
bundle_root_dir = "$_bundle_gen_dir/$_output_name$_bundle_extension"
bundle_contents_dir = bundle_root_dir
bundle_resources_dir = bundle_contents_dir
bundle_executable_dir = bundle_contents_dir
@ -470,6 +479,13 @@ template("ios_info_plist") {
# (optional) boolean, control whether code signing is enabled or not,
# default to ios_enable_code_signing if not defined.
#
# variants
# (optional) list of scopes, each scope needs to define the attributes
# "name" and "bundle_deps"; if defined and non-empty, then one bundle
# named $target_out_dir/$variant/$output_name will be created for each
# variant with the same binary but the correct bundle_deps, the bundle
# at $target_out_dir/$output_name will be a copy of the first variant.
#
# For more information, see "gn help executable".
template("ios_app_bundle") {
_output_name = target_name
@ -482,6 +498,45 @@ template("ios_app_bundle") {
_arch_executable_target = _target_name + "_arch_executable"
_lipo_executable_target = _target_name + "_executable"
if (defined(invoker.variants) && invoker.variants != []) {
_variants = []
foreach(_variant, invoker.variants) {
assert(defined(_variant.name) && _variant.name != "",
"name must be defined for all $target_name variants")
assert(defined(_variant.bundle_deps),
"bundle_deps must be defined for all $target_name variants")
_variants += [ {
name = _variant.name
bundle_deps = _variant.bundle_deps
target_name = "${_target_name}_variants_${_variant.name}"
bundle_gen_dir = "$root_out_dir/variants/${_variant.name}"
} ]
}
} else {
# If no variants are passed to the template, use a fake variant with
# no name to avoid duplicating code. As no variant can have an empty
# name except this fake variant, it is possible to know if a variant
# is fake or not.
_variants = [ {
name = ""
bundle_deps = []
target_name = _target_name
bundle_gen_dir = root_out_dir
} ]
}
_default_variant = _variants[0]
if (current_toolchain != default_toolchain) {
# For use of _variants and _default_variant for secondary toolchain to
# avoid the "Assignment had no effect" error from gn.
assert(_variants != [])
assert(_default_variant.target_name != "")
}
source_set(_arch_executable_source) {
forward_variables_from(invoker,
"*",
@ -611,7 +666,11 @@ template("ios_app_bundle") {
"testonly",
])
visibility = [ ":$_target_name" ]
visibility = []
foreach(_variant, _variants) {
visibility += [ ":${_variant.target_name}" ]
}
output_name = _output_name
arch_binary_target = ":$_arch_executable_target"
arch_binary_output = _output_name
@ -728,51 +787,79 @@ template("ios_app_bundle") {
}
}
create_signed_bundle(_target_name) {
forward_variables_from(invoker,
[
"bundle_deps",
"bundle_deps_filter",
"data_deps",
"deps",
"enable_code_signing",
"entitlements_path",
"entitlements_target",
"extra_system_frameworks",
"public_configs",
"public_deps",
"testonly",
"visibility",
])
foreach(_variant, _variants) {
create_signed_bundle(_variant.target_name) {
forward_variables_from(invoker,
[
"bundle_deps",
"bundle_deps_filter",
"data_deps",
"deps",
"enable_code_signing",
"entitlements_path",
"entitlements_target",
"extra_system_frameworks",
"public_configs",
"public_deps",
"testonly",
"visibility",
])
output_name = _output_name
bundle_binary_target = ":$_lipo_executable_target"
bundle_binary_output = _output_name
bundle_extension = _bundle_extension
product_type = _product_type
output_name = _output_name
bundle_gen_dir = _variant.bundle_gen_dir
bundle_binary_target = ":$_lipo_executable_target"
bundle_binary_output = _output_name
bundle_extension = _bundle_extension
product_type = _product_type
_generate_info_plist_outputs =
get_target_outputs(":$_generate_info_plist")
primary_info_plist = _generate_info_plist_outputs[0]
partial_info_plist = "$target_gen_dir/${_target_name}_partial_info.plist"
_generate_info_plist_outputs =
get_target_outputs(":$_generate_info_plist")
primary_info_plist = _generate_info_plist_outputs[0]
partial_info_plist =
"$target_gen_dir/${_variant.target_name}_partial_info.plist"
if (!defined(deps)) {
deps = []
}
deps += [ ":$_generate_info_plist" ]
if (!defined(deps)) {
deps = []
}
deps += [ ":$_generate_info_plist" ]
if (_write_pkg_info) {
if (!defined(bundle_deps)) {
bundle_deps = []
}
bundle_deps += [ ":$_bundle_data_pkg_info" ]
}
if (use_ios_simulator) {
if (!defined(data_deps)) {
data_deps = []
if (_write_pkg_info) {
bundle_deps += [ ":$_bundle_data_pkg_info" ]
}
data_deps += [ "//testing/iossim" ]
bundle_deps += _variant.bundle_deps
if (use_ios_simulator) {
if (!defined(data_deps)) {
data_deps = []
}
data_deps += [ "//testing/iossim" ]
}
}
}
if (_default_variant.name != "") {
_bundle_short_name = "$_output_name$_bundle_extension"
action(_target_name) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/config/ios/hardlink.py"
public_deps = []
foreach(_variant, _variants) {
public_deps += [ ":${_variant.target_name}" ]
}
sources = [
"${_default_variant.bundle_gen_dir}/$_bundle_short_name",
]
outputs = [
"$root_out_dir/$_bundle_short_name",
]
args = rebase_path(sources, root_out_dir) +
rebase_path(outputs, root_out_dir)
}
}
}