# Copyright 2015 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/apple/apple_info_plist.gni") import("//build/config/apple/symbols.gni") import("//build/config/mac/mac_sdk.gni") # Generates Info.plist files for Mac apps and frameworks. # # Arguments # # info_plist: # (optional) string, path to the Info.plist file that will be used for # the bundle. # # info_plist_target: # (optional) string, if the info_plist is generated from an action, # rather than a regular source file, specify the target name in lieu # of info_plist. The two arguments are mutually exclusive. # # executable_name: # string, name of the generated target used for the product # and executable name as specified in the output Info.plist. # # extra_substitutions: # (optional) string array, 'key=value' pairs for extra fields which are # specified in a source Info.plist template. template("mac_info_plist") { assert(defined(invoker.info_plist) != defined(invoker.info_plist_target), "Only one of info_plist or info_plist_target may be specified in " + target_name) if (defined(invoker.info_plist)) { _info_plist = invoker.info_plist } else { _info_plist_target_output = get_target_outputs(invoker.info_plist_target) _info_plist = _info_plist_target_output[0] } apple_info_plist(target_name) { format = "xml1" extra_substitutions = [] if (defined(invoker.extra_substitutions)) { extra_substitutions = invoker.extra_substitutions } extra_substitutions += [ "MAC_SDK_BUILD=$mac_sdk_version", "MAC_SDK_NAME=$mac_sdk_name$mac_sdk_version", "MACOSX_DEPLOYMENT_TARGET=$mac_deployment_target", "CHROMIUM_MIN_SYSTEM_VERSION=$mac_min_system_version", "XCODE_BUILD=$xcode_build", "XCODE_VERSION=$xcode_version", ] plist_templates = [ "//build/config/mac/BuildInfo.plist", _info_plist, ] if (defined(invoker.info_plist_target)) { deps = [ invoker.info_plist_target ] } forward_variables_from(invoker, [ "testonly", "executable_name", ]) } } # Template to package a shared library into a Mac framework bundle. # # By default, the bundle target this template generates does not link the # resulting framework into anything that depends on it. If a dependency wants # a link-time (as well as build-time) dependency on the framework bundle, # depend against "$target_name+link". If only the build-time dependency is # required (e.g., for copying into another bundle), then use "$target_name". # # Arguments # # framework_version: # string, version of the framework. Typically this is a # single letter, like "A". # # framework_contents: # list of string, top-level items in the framework. This is # the list of symlinks to create in the .framework directory that link # into Versions/Current/. # # info_plist: # (optional) string, path to the Info.plist file that will be used for # the bundle. # # info_plist_target: # (optional) string, if the info_plist is generated from an action, # rather than a regular source file, specify the target name in lieu # of info_plist. The two arguments are mutually exclusive. # # output_name: # (optional) string, name of the generated framework without the # .framework suffix. If omitted, defaults to target_name. # # extra_substitutions: # (optional) string array, 'key=value' pairs for extra fields which are # specified in a source Info.plist template. # # This template provides three targets for the resulting framework bundle. The # link-time behavior varies depending on which of the two targets below is # added as a dependency: # - $target_name only adds a build-time dependency. Targets that depend on # it will not link against the framework. # - $target_name+link adds a build-time and link-time dependency. Targets # that depend on it will link against the framework. # - $target_name+link_nested adds a build-time and link-time dependency, but # only on the shared library and not the fully-assembled framework bundle. # This should only be used for other nested binary components of the # framework bundle (e.g. Helpers) that themselves depend on the main shared # library of the framework bundle. # # The build-time-only dependency is used for when a target needs to use the # framework either only for resources, or because the target loads it at run- # time, via dlopen() or NSBundle. The link-time dependency will cause the # dependee to have the framework loaded by dyld at launch. # # Example of build-time only dependency: # # mac_framework_bundle("CoreTeleportation") { # sources = [ ... ] # } # # bundle_data("core_teleportation_bundle_data") { # deps = [ ":CoreTeleportation" ] # sources = [ "$root_out_dir/CoreTeleportation.framework" ] # outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ] # } # # app_bundle("GoatTeleporter") { # sources = [ ... ] # deps = [ # ":core_teleportation_bundle_data", # ] # } # # The GoatTeleporter.app will not directly link against # CoreTeleportation.framework, but it will be included in the bundle's # Frameworks directory. # # Example of link-time dependency: # # mac_framework_bundle("CoreTeleportation") { # sources = [ ... ] # ldflags = [ # "-install_name", # "@executable_path/../Frameworks/$target_name.framework" # ] # } # # bundle_data("core_teleportation_bundle_data") { # deps = [ ":CoreTeleportation+link" ] # sources = [ "$root_out_dir/CoreTeleportation.framework" ] # outputs = [ "{{bundle_contents_dir}}/Frameworks/{{source_file_part}}" ] # } # # app_bundle("GoatTeleporter") { # sources = [ ... ] # deps = [ # ":core_teleportation_bundle_data", # ] # } # # Note that the framework is still copied to the app's bundle, but dyld will # load this library when the app is launched because it uses the "+link" # target as a dependency. This also requires that the framework set its # install_name so that dyld can locate it. # # See "gn help shared_library" for more information on arguments supported # by shared library target. template("mac_framework_bundle") { assert(defined(invoker.deps) || defined(invoker.public_deps), "Dependencies must be specified for $target_name") assert(invoker.framework_version != "", "framework_version is required") assert(defined(invoker.framework_contents), "framework_contents is required") _info_plist_target = target_name + "_info_plist" mac_info_plist(_info_plist_target) { executable_name = target_name if (defined(invoker.output_name)) { executable_name = invoker.output_name } forward_variables_from(invoker, [ "extra_substitutions", "info_plist", "info_plist_target", "testonly", ]) } _info_plist_bundle_data = _info_plist_target + "_bundle_data" bundle_data(_info_plist_bundle_data) { forward_variables_from(invoker, [ "testonly" ]) sources = get_target_outputs(":$_info_plist_target") outputs = [ "{{bundle_resources_dir}}/Info.plist" ] public_deps = [ ":$_info_plist_target" ] } _target_name = target_name _output_name = target_name if (defined(invoker.output_name)) { _output_name = invoker.output_name } # Create a file to track the build dependency on the framework_version and # framework_contents variables. _framework_toc = [ "Version=" + invoker.framework_version, _output_name, ] + invoker.framework_contents _framework_contents = [ _output_name ] + invoker.framework_contents _framework_toc_file = "$target_out_dir/${target_name}.toc" write_file(_framework_toc_file, _framework_toc) # Create local variables for referencing different parts of the bundle. _framework_target = _target_name _framework_name = _output_name + ".framework" _framework_base_dir = "$root_out_dir/$_framework_name" _framework_root_dir = _framework_base_dir + "/Versions/${invoker.framework_version}" # Clean the entire framework if the framework_version changes. _version_file = "$target_out_dir/${target_name}_version" exec_script("//build/config/mac/prepare_framework_version.py", [ rebase_path(_version_file), rebase_path(_framework_base_dir), invoker.framework_version, ]) # Create the symlinks. _framework_package_target = target_name + "_package" action(_framework_package_target) { script = "//build/config/mac/package_framework.py" # The TOC file never needs to be read, since its contents are the values # of GN variables. It is only used to trigger this rule when the values # change. inputs = [ _framework_toc_file ] _stamp_file = "$target_out_dir/run_${_framework_package_target}.stamp" outputs = [ _stamp_file ] visibility = [ ":$_framework_target" ] args = [ "--framework", rebase_path(_framework_base_dir, root_build_dir), "--stamp", rebase_path(_stamp_file, root_build_dir), "--version", invoker.framework_version, "--contents", ] + _framework_contents # It is not possible to list _framework_contents as outputs, since # ninja does not properly stat symbolic links. # https://github.com/ninja-build/ninja/issues/1186 } _link_shared_library_target = target_name + "_shared_library" _shared_library_bundle_data = target_name + "_shared_library_bundle_data" shared_library(_link_shared_library_target) { forward_variables_from(invoker, "*", [ "assert_no_deps", "bundle_deps", "code_signing_enabled", "data_deps", "info_plist", "info_plist_target", "output_name", "visibility", ]) visibility = [ ":$_shared_library_bundle_data", ":${_framework_target}+link_nested", ] output_name = _output_name output_prefix_override = true output_extension = "" output_dir = "$target_out_dir/$_link_shared_library_target" } bundle_data(_shared_library_bundle_data) { visibility = [ ":$_framework_target" ] forward_variables_from(invoker, [ "testonly" ]) sources = [ "$target_out_dir/$_link_shared_library_target/$_output_name" ] outputs = [ "{{bundle_executable_dir}}/$_output_name" ] public_deps = [ ":$_link_shared_library_target" ] } _framework_public_config = _target_name + "_public_config" config(_framework_public_config) { visibility = [ ":$_framework_target+link" ] framework_dirs = [ root_out_dir ] frameworks = [ _framework_name ] } create_bundle(_framework_target) { forward_variables_from(invoker, [ "data_deps", "deps", "public_deps", "testonly", ]) if (defined(invoker.visibility)) { visibility = invoker.visibility visibility += [ ":$_target_name+link" ] } if (!defined(deps)) { deps = [] } deps += [ ":$_info_plist_bundle_data" ] if (defined(invoker.bundle_deps)) { deps += invoker.bundle_deps } if (!defined(public_deps)) { public_deps = [] } public_deps += [ ":$_framework_package_target", ":$_shared_library_bundle_data", ] if (enable_dsyms) { data = [ "$root_out_dir/$_output_name.dSYM/Contents/Info.plist", "$root_out_dir/$_output_name.dSYM/Contents/Resources/DWARF/$_output_name", ] } bundle_root_dir = _framework_base_dir bundle_contents_dir = _framework_root_dir bundle_resources_dir = "$bundle_contents_dir/Resources" bundle_executable_dir = bundle_contents_dir } group(_target_name + "+link") { forward_variables_from(invoker, [ "public_configs", "testonly", "visibility", ]) public_deps = [ ":$_target_name" ] if (!defined(public_configs)) { public_configs = [] } public_configs += [ ":$_framework_public_config" ] } group(_target_name + "+link_nested") { forward_variables_from(invoker, [ "public_configs", "testonly", "visibility", ]) # Depend only on the shared library. Nested code will be a dependency of # the create_bundle target, which would be cyclic with depending on the # framework itself. This is sufficient to link; for loading, a proper # install_name should be set. public_deps = [ ":$_link_shared_library_target" ] } } set_defaults("mac_framework_bundle") { configs = default_shared_library_configs } # Template to create a Mac executable application bundle. # # Arguments # # package_type: # (optional) string, the product package type to create. Options are: # "app" to create a .app bundle (default) # "xpc" to create an .xpc service bundle # # info_plist: # (optional) string, path to the Info.plist file that will be used for # the bundle. # # info_plist_target: # (optional) string, if the info_plist is generated from an action, # rather than a regular source file, specify the target name in lieu # of info_plist. The two arguments are mutually exclusive. # # output_name: # (optional) string, name of the generated app without the # .app suffix. If omitted, defaults to target_name. # # extra_configs: # (optional) list of label, additional configs to apply to the # executable target. # # remove_configs: # (optional) list of label, default configs to remove from the target. # # extra_substitutions: # (optional) string array, 'key=value' pairs for extra fields which are # specified in a source Info.plist template. template("mac_app_bundle") { _target_name = target_name _output_name = target_name if (defined(invoker.output_name)) { _output_name = invoker.output_name } _package_type = "app" if (defined(invoker.package_type)) { _package_type = invoker.package_type } if (_package_type == "app") { _output_extension = "app" _product_type = "com.apple.product-type.application" _write_pkg_info = true } else if (_package_type == "xpc") { _output_extension = "xpc" _product_type = "com.apple.product-type.xpc-service" _write_pkg_info = false } else if (_package_type == "bundle") { _output_extension = "bundle" _product_type = "com.apple.product-type.bundle" _write_pkg_info = false } else { assert(false, "Unsupported packge_type: " + packge_type) } _executable_target = target_name + "_executable" _executable_bundle_data = _executable_target + "_bundle_data" _info_plist_target = target_name + "_info_plist" mac_info_plist(_info_plist_target) { executable_name = _output_name forward_variables_from(invoker, [ "extra_substitutions", "info_plist", "info_plist_target", "testonly", ]) } if (_write_pkg_info) { _pkg_info_target = target_name + "_pkg_info" action(_pkg_info_target) { forward_variables_from(invoker, [ "testonly" ]) script = "//build/apple/write_pkg_info.py" inputs = [ "//build/apple/plist_util.py" ] sources = get_target_outputs(":$_info_plist_target") outputs = [ "$target_gen_dir/$_pkg_info_target" ] args = [ "--plist" ] + rebase_path(sources, root_build_dir) + [ "--output" ] + rebase_path(outputs, root_build_dir) deps = [ ":$_info_plist_target" ] } } executable(_executable_target) { visibility = [ ":$_executable_bundle_data" ] forward_variables_from(invoker, "*", [ "assert_no_deps", "data_deps", "info_plist", "output_name", "visibility", ]) if (defined(extra_configs)) { configs += extra_configs } if (defined(remove_configs)) { configs -= remove_configs } output_name = _output_name output_dir = "$target_out_dir/$_executable_target" } bundle_data(_executable_bundle_data) { visibility = [ ":$_target_name" ] forward_variables_from(invoker, [ "testonly" ]) sources = [ "$target_out_dir/$_executable_target/$_output_name" ] outputs = [ "{{bundle_executable_dir}}/$_output_name" ] public_deps = [ ":$_executable_target" ] } _info_plist_bundle_data = _info_plist_target + "_bundle_data" bundle_data(_info_plist_bundle_data) { forward_variables_from(invoker, [ "testonly" ]) visibility = [ ":$_target_name" ] sources = get_target_outputs(":$_info_plist_target") outputs = [ "{{bundle_contents_dir}}/Info.plist" ] public_deps = [ ":$_info_plist_target" ] } if (_write_pkg_info) { _pkg_info_bundle_data = _pkg_info_target + "_bundle_data" bundle_data(_pkg_info_bundle_data) { forward_variables_from(invoker, [ "testonly" ]) visibility = [ ":$_target_name" ] sources = get_target_outputs(":$_pkg_info_target") outputs = [ "{{bundle_contents_dir}}/PkgInfo" ] public_deps = [ ":$_pkg_info_target" ] } } create_bundle(_target_name) { forward_variables_from(invoker, [ "data_deps", "deps", "public_deps", "testonly", ]) if (!defined(deps)) { deps = [] } deps += [ ":$_executable_bundle_data", ":$_info_plist_bundle_data", ] if (_write_pkg_info) { deps += [ ":$_pkg_info_bundle_data" ] } if (enable_dsyms) { data = [ "$root_out_dir/$_output_name.dSYM/Contents/Info.plist", "$root_out_dir/$_output_name.dSYM/Contents/Resources/DWARF/$_output_name", ] } product_type = _product_type bundle_root_dir = "$root_out_dir/${_output_name}.${_output_extension}" bundle_contents_dir = "$bundle_root_dir/Contents" bundle_resources_dir = "$bundle_contents_dir/Resources" bundle_executable_dir = "$bundle_contents_dir/MacOS" } } # Template to package a loadable_module into a .plugin bundle. # # This takes no extra arguments that differ from a loadable_module. template("mac_plugin_bundle") { assert(defined(invoker.deps), "Dependencies must be specified for $target_name") _target_name = target_name _loadable_module_target = _target_name + "_loadable_module" _loadable_module_bundle_data = _loadable_module_target + "_bundle_data" _output_name = _target_name if (defined(invoker.output_name)) { _output_name = invoker.output_name } loadable_module(_loadable_module_target) { visibility = [ ":$_loadable_module_bundle_data" ] forward_variables_from(invoker, "*", [ "assert_no_deps", "data_deps", "output_name", "visibility", ]) output_dir = "$target_out_dir" output_name = _output_name } bundle_data(_loadable_module_bundle_data) { forward_variables_from(invoker, [ "testonly" ]) visibility = [ ":$_target_name" ] sources = [ "$target_out_dir/$_output_name.so" ] outputs = [ "{{bundle_executable_dir}}/$_output_name" ] public_deps = [ ":$_loadable_module_target" ] } create_bundle(_target_name) { forward_variables_from(invoker, [ "data_deps", "deps", "public_deps", "testonly", "visibility", ]) if (!defined(deps)) { deps = [] } deps += [ ":$_loadable_module_bundle_data" ] if (enable_dsyms) { data = [ "$root_out_dir/$_output_name.so.dSYM/Contents/Info.plist", "$root_out_dir/$_output_name.so.dSYM/Contents/Resources/DWARF/$_output_name.so", ] } bundle_root_dir = "$root_out_dir/$_output_name.plugin" bundle_contents_dir = "$bundle_root_dir/Contents" bundle_executable_dir = "$bundle_contents_dir/MacOS" } }