// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.rules.cpp; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildConfiguration.LabelConverter; import com.google.devtools.build.lib.analysis.config.CompilationMode; import com.google.devtools.build.lib.analysis.config.FragmentOptions; import com.google.devtools.build.lib.analysis.config.PerLabelOptions; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.rules.cpp.CppConfiguration.DynamicMode; import com.google.devtools.build.lib.rules.cpp.CppConfiguration.StripMode; import com.google.devtools.build.lib.util.OptionsUtils; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LipoMode; import com.google.devtools.common.options.Converter; import com.google.devtools.common.options.EnumConverter; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionsParser.OptionUsageRestrictions; import com.google.devtools.common.options.OptionsParsingException; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * Command-line options for C++. */ public class CppOptions extends FragmentOptions { /** * Converts a comma-separated list of compilation mode settings to a properly typed List. */ public static class FissionOptionConverter implements Converter<List<CompilationMode>> { @Override public List<CompilationMode> convert(String input) throws OptionsParsingException { ImmutableSet.Builder<CompilationMode> modes = ImmutableSet.builder(); if (input.equals("yes")) { // Special case: enable all modes. modes.add(CompilationMode.values()); } else if (!input.equals("no")) { // "no" is another special case that disables all modes. CompilationMode.Converter modeConverter = new CompilationMode.Converter(); for (String mode : Splitter.on(',').split(input)) { modes.add(modeConverter.convert(mode)); } } return modes.build().asList(); } @Override public String getTypeDescription() { return "a set of compilation modes"; } } /** * Converter for {@link DynamicMode} */ public static class DynamicModeConverter extends EnumConverter<DynamicMode> { public DynamicModeConverter() { super(DynamicMode.class, "dynamic mode"); } } /** * Converter for the --strip option. */ public static class StripModeConverter extends EnumConverter<StripMode> { public StripModeConverter() { super(StripMode.class, "strip mode"); } } private static final String LIBC_RELATIVE_LABEL = ":everything"; /** * Converts a String, which is a package label into a label that can be used for a LibcTop object. */ public static class LibcTopLabelConverter implements Converter<Label> { @Override public Label convert(String input) throws OptionsParsingException { if (input.equals("default")) { // This is needed for defining config_setting() values, the syntactic form // of which must be a String, to match absence of a --grte_top option. // "--grte_top=default" works on the command-line too, // but that's an inconsequential side-effect, not the intended purpose. return null; } if (!input.startsWith("//")) { throw new OptionsParsingException("Not a label"); } try { return Label.parseAbsolute(input).getRelative(LIBC_RELATIVE_LABEL); } catch (LabelSyntaxException e) { throw new OptionsParsingException(e.getMessage()); } } @Override public String getTypeDescription() { return "a label"; } } /** * Converter for the --lipo option. */ public static class LipoModeConverter extends EnumConverter<LipoMode> { public LipoModeConverter() { super(LipoMode.class, "LIPO mode"); } } @Option( name = "crosstool_top", defaultValue = "@bazel_tools//tools/cpp:toolchain", category = "version", converter = LabelConverter.class, help = "The label of the crosstool package to be used for compiling C++ code." ) public Label crosstoolTop; @Option( name = "compiler", defaultValue = "null", category = "version", help = "The C++ compiler to use for compiling the target." ) public String cppCompiler; @Option( name = "glibc", defaultValue = "null", category = "version", help = "The version of glibc the target should be linked against. " + "By default, a suitable version is chosen based on --cpu." ) public String glibc; // O intrepid reaper of unused options: Be warned that the [no]start_end_lib // option, however tempting to remove, has a use case. Look in our telemetry data. @Option( name = "start_end_lib", defaultValue = "true", category = "strategy", // but also adds edges to the action graph help = "Use the --start-lib/--end-lib ld options if supported by the toolchain." ) public boolean useStartEndLib; @Option( name = "interface_shared_objects", defaultValue = "true", category = "strategy", // but also adds edges to the action graph help = "Use interface shared objects if supported by the toolchain. " + "All ELF toolchains currently support this setting." ) public boolean useInterfaceSharedObjects; @Option( name = "fission", defaultValue = "no", converter = FissionOptionConverter.class, category = "semantics", help = "Specifies which compilation modes use fission for C++ compilations and links. " + " May be any combination of {'fastbuild', 'dbg', 'opt'} or the special values 'yes' " + " to enable all modes and 'no' to disable all modes." ) public List<CompilationMode> fissionModes; @Option( name = "build_test_dwp", defaultValue = "false", category = "semantics", help = "If enabled, when building C++ tests statically and with fission the .dwp file " + " for the test binary will be automatically built as well." ) public boolean buildTestDwp; @Option( name = "dynamic_mode", defaultValue = "default", converter = DynamicModeConverter.class, category = "semantics", help = "Determines whether C++ binaries will be linked dynamically. 'default' means " + "blaze will choose whether to link dynamically. 'fully' means all libraries " + "will be linked dynamically. 'off' means that all libraries will be linked " + "in mostly static mode." ) public DynamicMode dynamicMode; @Option( name = "experimental_link_compile_output_separately", defaultValue = "false", category = "semantics", help = "This flag is experimental and may go away at any time. " + "If true, dynamically linked binary targets will build and link their own srcs as " + "a dynamic library instead of directly linking it in." ) public boolean linkCompileOutputSeparately; @Option( name = "force_pic", defaultValue = "false", category = "semantics", help = "If enabled, all C++ compilations produce position-independent code (\"-fPIC\")," + " links prefer PIC pre-built libraries over non-PIC libraries, and links produce" + " position-independent executables (\"-pie\")." ) public boolean forcePic; @Option( name = "force_ignore_dash_static", defaultValue = "false", category = "semantics", help = "If set, '-static' options in the linkopts of cc_* rules will be ignored." ) public boolean forceIgnoreDashStatic; @Option( name = "experimental_skip_static_outputs", defaultValue = "false", category = "semantics", help = "This flag is experimental and may go away at any time. " + "If true, linker output for mostly-static C++ executables is a tiny amount of " + "dummy dependency information, and NOT a usable binary. Kludge, but can reduce " + "network and disk I/O load (and thus, continuous build cycle times) by a lot. " + "NOTE: use of this flag REQUIRES --distinct_host_configuration." ) public boolean skipStaticOutputs; // TODO(djasper): Remove once it has been removed from the global blazerc. @Option( name = "send_transitive_header_module_srcs", defaultValue = "true", category = "semantics", help = "Obsolete. Don't use." ) public boolean sendTransitiveHeaderModuleSrcs; @Option( name = "process_headers_in_dependencies", defaultValue = "false", category = "semantics", help = "When building a target //a:a, process headers in all targets that //a:a depends " + "on (if header processing is enabled for the toolchain)." ) public boolean processHeadersInDependencies; @Option( name = "copt", allowMultiple = true, defaultValue = "", category = "flags", help = "Additional options to pass to gcc." ) public List<String> coptList; @Option( name = "cxxopt", defaultValue = "", category = "flags", allowMultiple = true, help = "Additional option to pass to gcc when compiling C++ source files." ) public List<String> cxxoptList; @Option( name = "conlyopt", allowMultiple = true, defaultValue = "", category = "flags", help = "Additional option to pass to gcc when compiling C source files." ) public List<String> conlyoptList; @Option( name = "linkopt", defaultValue = "", category = "flags", allowMultiple = true, help = "Additional option to pass to gcc when linking." ) public List<String> linkoptList; @Option( name = "ltoindexopt", defaultValue = "", category = "flags", allowMultiple = true, help = "Additional option to pass to the LTO indexing step (under --features=thin_lto)." ) public List<String> ltoindexoptList; @Option( name = "stripopt", allowMultiple = true, defaultValue = "", category = "flags", help = "Additional options to pass to strip when generating a '<name>.stripped' binary." ) public List<String> stripoptList; @Option( name = "custom_malloc", defaultValue = "null", category = "semantics", help = "Specifies a custom malloc implementation. This setting overrides malloc " + "attributes in build rules.", converter = LabelConverter.class ) public Label customMalloc; @Option( name = "legacy_whole_archive", defaultValue = "true", category = "semantics", help = "When on, use --whole-archive for cc_binary rules that have " + "linkshared=1 and either linkstatic=1 or '-static' in linkopts. " + "This is for backwards compatibility only. " + "A better alternative is to use alwayslink=1 where required." ) public boolean legacyWholeArchive; @Option( name = "strip", defaultValue = "sometimes", category = "flags", help = "Specifies whether to strip binaries and shared libraries " + " (using \"-Wl,--strip-debug\"). The default value of 'sometimes'" + " means strip iff --compilation_mode=fastbuild.", converter = StripModeConverter.class ) public StripMode stripBinaries; @Option( name = "fdo_instrument", defaultValue = "null", converter = OptionsUtils.PathFragmentConverter.class, category = "flags", implicitRequirements = {"--copt=-Wno-error"}, help = "Generate binaries with FDO instrumentation. Specify the relative " + "directory name for the .gcda files at runtime with GCC compiler. " + "With Clang/LLVM compiler, it also accepts the directory name under" + "which the raw profile file(s) will be dumped at runtime." ) /** * Never read FDO/LIPO options directly. This is because {@link #lipoConfigurationState} * determines whether these options are actually "active" for this configuration. Instead, use * the equivalent getter method, which takes that into account. */ public PathFragment fdoInstrumentForBuild; /** * Returns the --fdo_instrument value if FDO is specified and active for this configuration, * the default value otherwise. */ public PathFragment getFdoInstrument() { return enableLipoSettings() ? fdoInstrumentForBuild : null; } @Option( name = "fdo_optimize", defaultValue = "null", category = "flags", help = "Use FDO profile information to optimize compilation. Specify the name " + "of the zip file containing the .gcda file tree or an afdo file containing " + "an auto profile. This flag also accepts files specified as labels, for " + "example //foo/bar:file.afdo. Such labels must refer to input files; you may " + "need to add an exports_files directive to the corresponding package to make " + "the file visible to Blaze. It also accepts a raw or an indexed LLVM profile file." ) /** * Never read FDO/LIPO options directly. This is because {@link #lipoConfigurationState} * determines whether these options are actually "active" for this configuration. Instead, use * the equivalent getter method, which takes that into account. */ public String fdoOptimizeForBuild; /** * Returns the --fdo_optimize value if FDO is specified and active for this configuration, * the default value otherwise. */ public String getFdoOptimize() { return enableLipoSettings() ? fdoOptimizeForBuild : null; } @Option( name = "autofdo_lipo_data", defaultValue = "false", category = "flags", help = "If true then the directory name for non-LIPO targets will have a " + "'-lipodata' suffix in AutoFDO mode." ) /** * Never read FDO/LIPO options directly. This is because {@link #lipoConfigurationState} * determines whether these options are actually "active" for this configuration. Instead, use * the equivalent getter method, which takes that into account. */ public boolean autoFdoLipoDataForBuild; /** * Returns the --autofdo_lipo_data value for this configuration. This is false except for data * configurations under LIPO builds. */ public boolean getAutoFdoLipoData() { return enableLipoSettings() ? autoFdoLipoDataForBuild : lipoModeForBuild != LipoMode.OFF && fdoOptimizeForBuild != null && FdoSupport.isAutoFdo( fdoOptimizeForBuild); } @Option( name = "lipo", defaultValue = "off", converter = LipoModeConverter.class, category = "flags", help = "Enable LIPO optimization (lightweight inter-procedural optimization, The allowed " + "values for this option are 'off' and 'binary', which enables LIPO. This option " + "only has an effect when FDO is also enabled. Currently LIPO is only supported " + "when building a single cc_binary rule." ) /** * Never read FDO/LIPO options directly. This is because {@link #lipoConfigurationState} * determines whether these options are actually "active" for this configuration. Instead, use * the equivalent getter method, which takes that into account. */ public LipoMode lipoModeForBuild; /** * Returns the --lipo value if LIPO is specified and active for this configuration, * the default value otherwise. */ public LipoMode getLipoMode() { return enableLipoSettings() ? lipoModeForBuild : LipoMode.OFF; } @Option( name = "lipo_context", defaultValue = "null", category = "flags", converter = LabelConverter.class, implicitRequirements = {"--linkopt=-Wl,--warn-unresolved-symbols"}, help = "Specifies the binary from which the LIPO profile information comes." ) /** * Never read FDO/LIPO options directly. This is because {@link #lipoConfigurationState} * determines whether these options are actually "active" for this configuration. Instead, use * the equivalent getter method, which takes that into account. */ public Label lipoContextForBuild; /** * Returns the --lipo_context value if LIPO is specified and active for this configuration, * null otherwise. */ @Nullable public Label getLipoContext() { return isLipoOptimization() ? lipoContextForBuild : null; } /** * Returns the LIPO context for this build, even if LIPO isn't enabled in the current * configuration. */ public Label getLipoContextForBuild() { return lipoContextForBuild; } /** * Internal state determining how to apply LIPO settings under this configuration. */ public enum LipoConfigurationState { /** Don't LIPO-optimize targets under this configuration. */ IGNORE_LIPO, /** LIPO-optimize targets under this configuration if this is a LIPO build. */ APPLY_LIPO, /** * Evaluate targets in this configuration in "LIPO context collector" mode. See * {@link FdoSupport} for details. */ LIPO_CONTEXT_COLLECTOR, } /** * Converter for {@link LipoConfigurationState}. */ public static class LipoConfigurationStateConverter extends EnumConverter<LipoConfigurationState> { public LipoConfigurationStateConverter() { super(LipoConfigurationState.class, "LIPO configuration state"); } } @Option( name = "lipo configuration state", defaultValue = "apply_lipo", optionUsageRestrictions = OptionUsageRestrictions.INTERNAL, converter = LipoConfigurationStateConverter.class ) public LipoConfigurationState lipoConfigurationState; /** * Returns true if targets under this configuration should use the build's LIPO settings. * * <p>Even when we switch off LIPO (e.g. by switching to a data configuration), we still need to * remember the LIPO settings in case we need to switch back (e.g. if we build a genrule with a * data dependency on the LIPO context). * * <p>We achieve this by maintaining a "configuration state" flag that flips on / off when we * want to enable / disable LIPO respectively. This means we need to be careful to distinguish * between the existence of LIPO settings and LIPO actually applying to the configuration. So when * buiding a target, it's not enough to see if {@link #lipoContextForBuild} or * {@link #lipoModeForBuild} are set. We also need to check this flag. * * <p>This class exposes appropriate convenience methods to make these checks convenient and easy. * Use them and read the documentation carefully. */ private boolean enableLipoSettings() { return lipoConfigurationState != LipoConfigurationState.IGNORE_LIPO; } @Option( name = "experimental_stl", converter = LabelConverter.class, defaultValue = "null", category = "version", help = "If set, use this label instead of the default STL implementation. " + "This option is EXPERIMENTAL and may go away in a future release." ) public Label stl; @Option( name = "save_temps", defaultValue = "false", category = "what", help = "If set, temporary outputs from gcc will be saved. " + "These include .s files (assembler code), .i files (preprocessed C) and " + ".ii files (preprocessed C++)." ) public boolean saveTemps; @Option( name = "per_file_copt", allowMultiple = true, converter = PerLabelOptions.PerLabelOptionsConverter.class, defaultValue = "", category = "semantics", help = "Additional options to selectively pass to gcc when compiling certain files. " + "This option can be passed multiple times. " + "Syntax: regex_filter@option_1,option_2,...,option_n. Where regex_filter stands " + "for a list of include and exclude regular expression patterns (Also see " + "--instrumentation_filter). option_1 to option_n stand for " + "arbitrary command line options. If an option contains a comma it has to be " + "quoted with a backslash. Options can contain @. Only the first @ is used to " + "split the string. Example: " + "--per_file_copt=//foo/.*\\.cc,-//foo/bar\\.cc@-O0 adds the -O0 " + "command line option to the gcc command line of all cc files in //foo/ " + "except bar.cc." ) public List<PerLabelOptions> perFileCopts; @Option( name = "host_crosstool_top", defaultValue = "null", converter = LabelConverter.class, category = "semantics", help = "By default, the --crosstool_top and --compiler options are also used " + "for the host configuration. If this flag is provided, Blaze uses the default libc " + "and compiler for the given crosstool_top." ) public Label hostCrosstoolTop; @Option( name = "host_copt", allowMultiple = true, defaultValue = "", category = "flags", help = "Additional options to pass to gcc for host tools." ) public List<String> hostCoptList; @Option( name = "host_cxxopt", allowMultiple = true, defaultValue = "", category = "flags", help = "Additional options to pass to gcc for host tools." ) public List<String> hostCxxoptList; @Option( name = "grte_top", defaultValue = "null", // The default value is chosen by the toolchain. category = "version", converter = LibcTopLabelConverter.class, help = "A label to a checked-in libc library. The default value is selected by the crosstool " + "toolchain, and you almost never need to override it." ) public Label libcTopLabel; @Option( name = "host_grte_top", defaultValue = "null", // The default value is chosen by the toolchain. category = "version", converter = LibcTopLabelConverter.class, help = "If specified, this setting overrides the libc top-level directory (--grte_top) " + "for the host configuration." ) public Label hostLibcTopLabel; @Option( name = "output_symbol_counts", defaultValue = "false", category = "flags", help = "If enabled, for every C++ binary linked with gold, the number of defined symbols " + "and the number of used symbols per input file is stored in a .sc file." ) public boolean symbolCounts; @Option( name = "experimental_inmemory_dotd_files", defaultValue = "false", category = "experimental", help = "If enabled, C++ .d files will be passed through in memory directly from the remote " + "build nodes instead of being written to disk." ) public boolean inmemoryDotdFiles; @Option( name = "experimental_skip_unused_modules", defaultValue = "false", category = "experimental", help = "Deprecated. No effect." ) public boolean skipUnusedModules; @Option( name = "experimental_prune_more_modules", defaultValue = "false", category = "experimental", help = "Deprecated. No effect." ) public boolean pruneMoreModules; @Option( name = "prune_cpp_modules", defaultValue = "true", category = "strategy", help = "If enabled, use the results of input discovery to reduce the number of used modules." ) public boolean pruneCppModules; @Option( name = "parse_headers_verifies_modules", defaultValue = "false", category = "strategy", help = "If enabled, the parse_headers feature verifies that a header module can be built for the " + "target in question instead of doing a separate compile of the header." ) public boolean parseHeadersVerifiesModules; @Option( name = "experimental_omitfp", defaultValue = "false", category = "semantics", help = "If true, use libunwind for stack unwinding, and compile with " + "-fomit-frame-pointer and -fasynchronous-unwind-tables." ) public boolean experimentalOmitfp; @Option( name = "share_native_deps", defaultValue = "true", category = "strategy", help = "If true, native libraries that contain identical functionality " + "will be shared among different targets" ) public boolean shareNativeDeps; @Option( name = "strict_system_includes", defaultValue = "false", category = "strategy", help = "If true, headers found through system include paths (-isystem) are also required to be " + "declared." ) public boolean strictSystemIncludes; @Override public FragmentOptions getHost(boolean fallback) { CppOptions host = (CppOptions) getDefault(); // The crosstool options are partially copied from the target configuration. if (!fallback) { if (hostCrosstoolTop == null) { host.cppCompiler = cppCompiler; host.crosstoolTop = crosstoolTop; host.glibc = glibc; } else { host.crosstoolTop = hostCrosstoolTop; } } // hostLibcTop doesn't default to the target's libcTop. // Only an explicit command-line option will change it. // The default is whatever the host's crosstool (which might have been specified // by --host_crosstool_top, or --crosstool_top as a fallback) says it should be. host.libcTopLabel = hostLibcTopLabel; // -g0 is the default, but allowMultiple options cannot have default values so we just pass // -g0 first and let the user options override it. host.coptList = ImmutableList.<String>builder().add("-g0").addAll(hostCoptList).build(); host.cxxoptList = ImmutableList.<String>builder().add("-g0").addAll(hostCxxoptList).build(); host.useStartEndLib = useStartEndLib; host.stripBinaries = StripMode.ALWAYS; host.fdoOptimizeForBuild = null; host.lipoModeForBuild = LipoMode.OFF; host.inmemoryDotdFiles = inmemoryDotdFiles; return host; } @Override public void addAllLabels(Multimap<String, Label> labelMap) { labelMap.put("crosstool", crosstoolTop); if (hostCrosstoolTop != null) { labelMap.put("crosstool", hostCrosstoolTop); } if (libcTopLabel != null) { Label libcLabel = libcTopLabel; if (libcLabel != null) { labelMap.put("crosstool", libcLabel); } } if (hostLibcTopLabel != null) { Label libcLabel = hostLibcTopLabel; if (libcLabel != null) { labelMap.put("crosstool", libcLabel); } } addOptionalLabel(labelMap, "fdo", getFdoOptimize()); if (stl != null) { labelMap.put("STL", stl); } if (customMalloc != null) { labelMap.put("custom_malloc", customMalloc); } if (getLipoContext() != null) { labelMap.put("lipo", getLipoContext()); } } @Override public Map<String, Set<Label>> getDefaultsLabels(BuildConfiguration.Options commonOptions) { Set<Label> crosstoolLabels = new LinkedHashSet<>(); crosstoolLabels.add(crosstoolTop); if (hostCrosstoolTop != null) { crosstoolLabels.add(hostCrosstoolTop); } if (libcTopLabel != null) { Label libcLabel = libcTopLabel; if (libcLabel != null) { crosstoolLabels.add(libcLabel); } } return ImmutableMap.of("CROSSTOOL", crosstoolLabels, "COVERAGE", ImmutableSet.<Label>of()); } /** * Returns true if targets under this configuration should apply FDO. */ public boolean isFdo() { return getFdoOptimize() != null || getFdoInstrument() != null; } /** * Returns true if this configuration has LIPO optimization settings (even if they're * not necessarily active). */ private boolean hasLipoOptimizationState() { return lipoModeForBuild == LipoMode.BINARY && fdoOptimizeForBuild != null && lipoContextForBuild != null; } /** * Returns true if targets under this configuration should LIPO-optimize. */ public boolean isLipoOptimization() { return hasLipoOptimizationState() && enableLipoSettings() && !isLipoContextCollector(); } /** * Returns true if this is a data configuration for a LIPO-optimizing build. * * <p>This means LIPO is not applied for this configuration, but LIPO might be reenabled further * down the dependency tree. */ public boolean isDataConfigurationForLipoOptimization() { return hasLipoOptimizationState() && !enableLipoSettings(); } /** * Returns true if targets under this configuration should LIPO-optimize or LIPO-instrument. */ public boolean isLipoOptimizationOrInstrumentation() { return getLipoMode() == LipoMode.BINARY && ((getFdoOptimize() != null && getLipoContext() != null) || getFdoInstrument() != null) && !isLipoContextCollector(); } /** * Returns true if this is the LIPO context collector configuration. */ public boolean isLipoContextCollector() { return lipoConfigurationState == LipoConfigurationState.LIPO_CONTEXT_COLLECTOR; } /** * FDO/LIPO is not yet compatible with dynamic configurations. **/ @Override public boolean useStaticConfigurationsOverride() { // --lipo=binary is technically possible without FDO, even though it doesn't do anything. return isFdo() || lipoModeForBuild == LipoMode.BINARY; } @Override public boolean enableActions() { return !isLipoContextCollector(); } }