Java tutorial
// Copyright 2017 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.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.devtools.build.lib.analysis.config.CompilationMode; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.rules.cpp.CppActionConfigs.CppPlatform; import com.google.devtools.build.lib.rules.cpp.CppConfiguration.FlagList; import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain.ArtifactNamePattern; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LinkingModeFlags; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.LipoMode; import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.ToolPath; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.TextFormat; import com.google.protobuf.TextFormat.ParseException; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Information describing the C++ compiler derived from the CToolchain proto. * * <p>This wrapper class is used to re-plumb information so that it's eventually accessed through * {@link CcToolchainProvider} instead of {@link CppConfiguration}. */ public final class CppToolchainInfo { private final PathFragment crosstoolTopPathFragment; private final String toolchainIdentifier; private final CcToolchainFeatures toolchainFeatures; private final ImmutableMap<String, PathFragment> toolPaths; private final String compiler; private final PathFragment ldExecutable; private final String abiGlibcVersion; private final String targetCpu; private final String targetOS; private final ImmutableList<String> rawBuiltInIncludeDirectories; private final PathFragment defaultSysroot; private final PathFragment runtimeSysroot; private final String targetLibc; private final String hostSystemName; private final FlagList dynamicLibraryLinkFlags; private final ImmutableList<String> commonLinkOptions; private final ImmutableListMultimap<LinkingMode, String> linkOptionsFromLinkingMode; private final ImmutableListMultimap<LipoMode, String> linkOptionsFromLipoMode; private final ImmutableListMultimap<CompilationMode, String> linkOptionsFromCompilationMode; private final ImmutableList<String> testOnlyLinkFlags; private final ImmutableList<String> ldOptions; private final ImmutableList<String> objcopyOptions; private final Label staticRuntimeLibsLabel; private final Label dynamicRuntimeLibsLabel; private final String solibDirectory; private final String abi; private final String targetSystemName; private final ImmutableMap<String, String> additionalMakeVariables; private final boolean supportsFission; private final boolean supportsStartEndLib; private final boolean supportsEmbeddedRuntimes; private final boolean supportsDynamicLinker; private final boolean supportsInterfaceSharedObjects; private final boolean supportsGoldLinker; private final boolean toolchainNeedsPic; /** Creates a CppToolchainInfo from a toolchain. */ public CppToolchainInfo(CToolchain cToolchain, PathFragment crosstoolTopPathFragment, Label toolchainLabel) throws InvalidConfigurationException { CToolchain toolchain = cToolchain; this.crosstoolTopPathFragment = crosstoolTopPathFragment; this.hostSystemName = toolchain.getHostSystemName(); this.compiler = toolchain.getCompiler(); this.targetCpu = toolchain.getTargetCpu(); this.targetSystemName = toolchain.getTargetSystemName(); this.targetLibc = toolchain.getTargetLibc(); this.targetOS = toolchain.getCcTargetOs(); try { this.staticRuntimeLibsLabel = toolchainLabel .getRelative(toolchain.hasStaticRuntimesFilegroup() ? toolchain.getStaticRuntimesFilegroup() : "static-runtime-libs-" + targetCpu); this.dynamicRuntimeLibsLabel = toolchainLabel .getRelative(toolchain.hasDynamicRuntimesFilegroup() ? toolchain.getDynamicRuntimesFilegroup() : "dynamic-runtime-libs-" + targetCpu); } catch (LabelSyntaxException e) { // All of the above label.getRelative() calls are valid labels, and the crosstool_top // was already checked earlier in the process. throw new AssertionError(e); } // Needs to be set before the first call to isLLVMCompiler(). this.toolchainIdentifier = toolchain.getToolchainIdentifier(); CrosstoolConfigurationIdentifier crosstoolConfig = CrosstoolConfigurationIdentifier .fromToolchain(toolchain); Preconditions.checkState(crosstoolConfig.getCpu().equals(targetCpu)); Preconditions.checkState(crosstoolConfig.getCompiler().equals(compiler)); Preconditions.checkState(crosstoolConfig.getLibc().equals(targetLibc)); this.solibDirectory = "_solib_" + targetCpu; this.supportsEmbeddedRuntimes = toolchain.getSupportsEmbeddedRuntimes(); toolchain = addLegacyFeatures(toolchain); this.toolchainFeatures = new CcToolchainFeatures(toolchain); this.supportsGoldLinker = toolchain.getSupportsGoldLinker(); this.supportsStartEndLib = toolchain.getSupportsStartEndLib(); this.supportsInterfaceSharedObjects = toolchain.getSupportsInterfaceSharedObjects(); this.supportsFission = toolchain.getSupportsFission(); Map<String, PathFragment> toolPathsCollector = Maps.newHashMap(); for (CrosstoolConfig.ToolPath tool : toolchain.getToolPathList()) { PathFragment path = PathFragment.create(tool.getPath()); if (!path.isNormalized()) { throw new IllegalArgumentException("The include path '" + tool.getPath() + "' is not normalized."); } toolPathsCollector.put(tool.getName(), crosstoolTopPathFragment.getRelative(path)); } if (toolPathsCollector.isEmpty()) { // If no paths are specified, we just use the names of the tools as the path. for (Tool tool : Tool.values()) { toolPathsCollector.put(tool.getNamePart(), crosstoolTopPathFragment.getRelative(tool.getNamePart())); } } else { Iterable<Tool> neededTools = Iterables.filter(EnumSet.allOf(Tool.class), tool -> { if (tool == Tool.DWP) { // When fission is unsupported, don't check for the dwp tool. return supportsFission(); } else if (tool == Tool.LLVM_PROFDATA) { // TODO(tmsriram): Fix this to check if this is a llvm crosstool // and return true. This needs changes to crosstool_config.proto. return false; } else if (tool == Tool.GCOVTOOL || tool == Tool.OBJCOPY) { // gcov-tool and objcopy are optional, don't check whether they're present return false; } else { return true; } }); for (Tool tool : neededTools) { if (!toolPathsCollector.containsKey(tool.getNamePart())) { throw new IllegalArgumentException("Tool path for '" + tool.getNamePart() + "' is missing"); } } } this.toolPaths = ImmutableMap.copyOf(toolPathsCollector); ImmutableListMultimap.Builder<CompilationMode, String> linkOptionsFromCompilationModeBuilder = ImmutableListMultimap .builder(); for (CrosstoolConfig.CompilationModeFlags flags : toolchain.getCompilationModeFlagsList()) { // Remove this when CROSSTOOL files no longer contain 'coverage'. if (flags.getMode() == CrosstoolConfig.CompilationMode.COVERAGE) { continue; } CompilationMode realmode = CppConfiguration.importCompilationMode(flags.getMode()); linkOptionsFromCompilationModeBuilder.putAll(realmode, flags.getLinkerFlagList()); } linkOptionsFromCompilationMode = linkOptionsFromCompilationModeBuilder.build(); ImmutableListMultimap.Builder<LipoMode, String> linkOptionsFromLipoModeBuilder = ImmutableListMultimap .builder(); for (CrosstoolConfig.LipoModeFlags flags : toolchain.getLipoModeFlagsList()) { LipoMode realmode = flags.getMode(); linkOptionsFromLipoModeBuilder.putAll(realmode, flags.getLinkerFlagList()); } linkOptionsFromLipoMode = linkOptionsFromLipoModeBuilder.build(); ImmutableListMultimap.Builder<LinkingMode, String> linkOptionsFromLinkingModeBuilder = ImmutableListMultimap .builder(); // If a toolchain supports dynamic libraries at all, there must be at least one // of the following: // - a "DYNAMIC" section in linking_mode_flags (even if no flags are needed) // - a non-empty list in one of the dynamicLibraryLinkerFlag fields // If none of the above contain data, then the toolchain can't do dynamic linking. boolean haveDynamicMode = false; for (LinkingModeFlags flags : toolchain.getLinkingModeFlagsList()) { LinkingMode realmode = CppConfiguration.importLinkingMode(flags.getMode()); if (realmode == LinkingMode.DYNAMIC) { haveDynamicMode = true; } linkOptionsFromLinkingModeBuilder.putAll(realmode, flags.getLinkerFlagList()); } linkOptionsFromLinkingMode = linkOptionsFromLinkingModeBuilder.build(); this.commonLinkOptions = ImmutableList.copyOf(toolchain.getLinkerFlagList()); List<String> linkerFlagList = toolchain.getDynamicLibraryLinkerFlagList(); List<CToolchain.OptionalFlag> optionalLinkerFlagList = toolchain.getOptionalDynamicLibraryLinkerFlagList(); if (!linkerFlagList.isEmpty() || !optionalLinkerFlagList.isEmpty()) { haveDynamicMode = true; } this.supportsDynamicLinker = haveDynamicMode; dynamicLibraryLinkFlags = new FlagList(ImmutableList.copyOf(linkerFlagList), CppConfiguration.convertOptionalOptions(optionalLinkerFlagList), ImmutableList.<String>of()); this.objcopyOptions = ImmutableList.copyOf(toolchain.getObjcopyEmbedFlagList()); this.ldOptions = ImmutableList.copyOf(toolchain.getLdEmbedFlagList()); this.abi = toolchain.getAbiVersion(); this.abiGlibcVersion = toolchain.getAbiLibcVersion(); this.defaultSysroot = CppConfiguration.computeDefaultSysroot(toolchain); this.rawBuiltInIncludeDirectories = ImmutableList.copyOf(toolchain.getCxxBuiltinIncludeDirectoryList()); // The default value for optional string attributes is the empty string. // The runtime sysroot should really be set from --grte_top. However, currently libc has no // way to set the sysroot. The CROSSTOOL file does set the runtime sysroot, in the // builtin_sysroot field. This implies that you can not arbitrarily mix and match Crosstool // and libc versions, you must always choose compatible ones. runtimeSysroot = defaultSysroot; this.testOnlyLinkFlags = ImmutableList.copyOf(toolchain.getTestOnlyLinkerFlagList()); this.toolchainNeedsPic = toolchain.getNeedsPic(); Map<String, String> makeVariablesBuilder = new HashMap<>(); // The following are to be used to allow some build rules to avoid the limits on stack frame // sizes and variable-length arrays. Ensure that these are always set. makeVariablesBuilder.put("STACK_FRAME_UNLIMITED", ""); makeVariablesBuilder.put(CppConfiguration.CC_FLAGS_MAKE_VARIABLE_NAME, ""); for (CrosstoolConfig.MakeVariable variable : toolchain.getMakeVariableList()) { makeVariablesBuilder.put(variable.getName(), variable.getValue()); } this.additionalMakeVariables = ImmutableMap.copyOf(makeVariablesBuilder); this.ldExecutable = getToolPathFragment(CppConfiguration.Tool.LD); } // TODO(bazel-team): Remove this once bazel supports all crosstool flags through // feature configuration, and all crosstools have been converted. private CToolchain addLegacyFeatures(CToolchain toolchain) { CToolchain.Builder toolchainBuilder = CToolchain.newBuilder(); Set<ArtifactCategory> definedCategories = new HashSet<>(); for (ArtifactNamePattern pattern : toolchainBuilder.getArtifactNamePatternList()) { try { definedCategories.add(ArtifactCategory.valueOf(pattern.getCategoryName().toUpperCase())); } catch (IllegalArgumentException e) { // Invalid category name, will be detected later. continue; } } for (ArtifactCategory category : ArtifactCategory.values()) { if (!definedCategories.contains(category) && category.getDefaultPattern() != null) { toolchainBuilder.addArtifactNamePattern( ArtifactNamePattern.newBuilder().setCategoryName(category.toString().toLowerCase()) .setPattern(category.getDefaultPattern()).build()); } } ImmutableSet<String> featureNames = toolchain.getFeatureList().stream().map(feature -> feature.getName()) .collect(ImmutableSet.toImmutableSet()); if (!featureNames.contains(CppRuleClasses.NO_LEGACY_FEATURES)) { try { String gccToolPath = "DUMMY_GCC_TOOL"; String linkerToolPath = "DUMMY_LINKER_TOOL"; String arToolPath = "DUMMY_AR_TOOL"; String stripToolPath = "DUMMY_STRIP_TOOL"; for (ToolPath tool : toolchain.getToolPathList()) { if (tool.getName().equals(Tool.GCC.getNamePart())) { gccToolPath = tool.getPath(); linkerToolPath = crosstoolTopPathFragment.getRelative(PathFragment.create(tool.getPath())) .getPathString(); } if (tool.getName().equals(Tool.AR.getNamePart())) { arToolPath = tool.getPath(); } if (tool.getName().equals(Tool.STRIP.getNamePart())) { stripToolPath = tool.getPath(); } } // TODO(b/30109612): Remove fragile legacyCompileFlags shuffle once there are no legacy // crosstools. // Existing projects depend on flags from legacy toolchain fields appearing first on the // compile command line. 'legacy_compile_flags' feature contains all these flags, and so it // needs to appear before other features from {@link CppActionConfigs}. CToolchain.Feature legacyCompileFlagsFeature = toolchain.getFeatureList().stream() .filter(feature -> feature.getName().equals(CppRuleClasses.LEGACY_COMPILE_FLAGS)) .findFirst().orElse(null); if (legacyCompileFlagsFeature != null) { toolchainBuilder.addFeature(legacyCompileFlagsFeature); toolchain = removeLegacyCompileFlagsFeatureFromToolchain(toolchain); } TextFormat.merge( CppActionConfigs.getCppActionConfigs( getTargetLibc().equals("macosx") ? CppPlatform.MAC : CppPlatform.LINUX, featureNames, gccToolPath, linkerToolPath, arToolPath, stripToolPath, supportsEmbeddedRuntimes, toolchain.getSupportsInterfaceSharedObjects()), toolchainBuilder); } catch (ParseException e) { // Can only happen if we change the proto definition without changing our // configuration above. throw new RuntimeException(e); } } toolchainBuilder.mergeFrom(toolchain); if (!featureNames.contains(CppRuleClasses.NO_LEGACY_FEATURES)) { try { TextFormat.merge(CppActionConfigs.getFeaturesToAppearLastInToolchain(featureNames), toolchainBuilder); } catch (ParseException e) { // Can only happen if we change the proto definition without changing our // configuration above. throw new RuntimeException(e); } } return toolchainBuilder.build(); } private CToolchain removeLegacyCompileFlagsFeatureFromToolchain(CToolchain toolchain) { FieldDescriptor featuresFieldDescriptor = CToolchain.getDescriptor().findFieldByName("feature"); return toolchain.toBuilder() .setField(featuresFieldDescriptor, toolchain.getFeatureList().stream() .filter(feature -> !feature.getName().equals(CppRuleClasses.LEGACY_COMPILE_FLAGS)) .collect(ImmutableList.toImmutableList())) .build(); } ImmutableList<String> configureLinkerOptions(CompilationMode compilationMode, LipoMode lipoMode, LinkingMode linkingMode, PathFragment ldExecutable) { List<String> result = new ArrayList<>(); result.addAll(commonLinkOptions); result.addAll(linkOptionsFromCompilationMode.get(compilationMode)); result.addAll(linkOptionsFromLipoMode.get(lipoMode)); result.addAll(linkOptionsFromLinkingMode.get(linkingMode)); return ImmutableList.copyOf(result); } /** * Returns the toolchain identifier, which uniquely identifies the compiler version, target libc * version, target cpu, and LIPO linkage. */ public String getToolchainIdentifier() { return toolchainIdentifier; } /** Returns the path of the crosstool. */ public PathFragment getCrosstoolTopPathFragment() { return crosstoolTopPathFragment; } /** Returns the system name which is required by the toolchain to run. */ public String getHostSystemName() { return hostSystemName; } @Override public String toString() { return toolchainIdentifier; } /** Returns the compiler version string (e.g. "gcc-4.1.1"). */ public String getCompiler() { return compiler; } /** Returns the libc version string (e.g. "glibc-2.2.2"). */ public String getTargetLibc() { return targetLibc; } /** Returns the target architecture using blaze-specific constants (e.g. "piii"). */ public String getTargetCpu() { return targetCpu; } /** Unused, for compatibility with things internal to Google. */ public String getTargetOS() { return targetOS; } /** * Returns the path fragment that is either absolute or relative to the execution root that can be * used to execute the given tool. * * <p>Note that you must not use this method to get the linker location, but use {@link * #getLdExecutable} instead! */ public PathFragment getToolPathFragment(CppConfiguration.Tool tool) { return toolPaths.get(tool.getNamePart()); } /** * Returns a label that references the library files needed to statically link the C++ runtime * (i.e. libgcc.a, libgcc_eh.a, libstdc++.a) for the target architecture. */ public Label getStaticRuntimeLibsLabel() { return supportsEmbeddedRuntimes() ? staticRuntimeLibsLabel : null; } /** * Returns a label that references the library files needed to dynamically link the C++ runtime * (i.e. libgcc_s.so, libstdc++.so) for the target architecture. */ public Label getDynamicRuntimeLibsLabel() { return supportsEmbeddedRuntimes() ? dynamicRuntimeLibsLabel : null; } /** * Returns the abi we're using, which is a gcc version. E.g.: "gcc-3.4". Note that in practice we * might be using gcc-3.4 as ABI even when compiling with gcc-4.1.0, because ABIs are backwards * compatible. */ // TODO(bazel-team): The javadoc should clarify how this is used in Blaze. public String getAbi() { return abi; } /** * Returns the glibc version used by the abi we're using. This is a glibc version number (e.g., * "2.2.2"). Note that in practice we might be using glibc 2.2.2 as ABI even when compiling with * gcc-4.2.2, gcc-4.3.1, or gcc-4.4.0 (which use glibc 2.3.6), because ABIs are backwards * compatible. */ // TODO(bazel-team): The javadoc should clarify how this is used in Blaze. public String getAbiGlibcVersion() { return abiGlibcVersion; } /** * Returns the configured features of the toolchain. Rules should not call this directly, but * instead use {@code CcToolchainProvider.getFeatures}. */ public CcToolchainFeatures getFeatures() { return toolchainFeatures; } /** Returns whether the toolchain supports the gold linker. */ public boolean supportsGoldLinker() { return supportsGoldLinker; } /** Returns whether the toolchain supports the --start-lib/--end-lib options. */ public boolean supportsStartEndLib() { return supportsStartEndLib; } /** Returns whether the toolchain supports dynamic linking. */ public boolean supportsDynamicLinker() { return supportsDynamicLinker; } /** * Returns whether this toolchain supports interface shared objects. * * <p>Should be true if this toolchain generates ELF objects. */ public boolean supportsInterfaceSharedObjects() { return supportsInterfaceSharedObjects; } /** * Returns whether the toolchain supports linking C/C++ runtime libraries supplied inside the * toolchain distribution. */ public boolean supportsEmbeddedRuntimes() { return supportsEmbeddedRuntimes; } /** * Returns whether the toolchain supports "Fission" C++ builds, i.e. builds where compilation * partitions object code and debug symbols into separate output files. */ public boolean supportsFission() { return supportsFission; } /** * Returns whether shared libraries must be compiled with position independent code on this * platform. */ public boolean toolchainNeedsPic() { return toolchainNeedsPic; } /** * Returns the run time sysroot, which is where the dynamic linker and system libraries are found * at runtime. This is usually an absolute path. If the toolchain compiler does not support * sysroots, then this method returns <code>null</code>. */ public PathFragment getRuntimeSysroot() { return runtimeSysroot; } /** * Returns link options for the specified flag list, combined with universal options for all * shared libraries (regardless of link staticness). */ ImmutableList<String> getSharedLibraryLinkOptions(FlagList flags, Iterable<String> features) { return ImmutableList.<String>builder().addAll(flags.evaluate(features)) .addAll(dynamicLibraryLinkFlags.evaluate(features)).build(); } /** * Returns test-only link options such that certain test-specific features can be configured * separately (e.g. lazy binding). */ public ImmutableList<String> getTestOnlyLinkOptions() { return testOnlyLinkFlags; } /** * Returns the list of options to be used with 'objcopy' when converting binary files to object * files, or {@code null} if this operation is not supported. */ public ImmutableList<String> getObjCopyOptionsForEmbedding() { return objcopyOptions; } /** * Returns the list of options to be used with 'ld' when converting binary files to object files, * or {@code null} if this operation is not supported. */ public ImmutableList<String> getLdOptionsForEmbedding() { return ldOptions; } /** * Returns a map of additional make variables for use by {@link BuildConfiguration}. These are to * used to allow some build rules to avoid the limits on stack frame sizes and variable-length * arrays. * * <p>The returned map must contain an entry for {@code STACK_FRAME_UNLIMITED}, though the entry * may be an empty string. */ public ImmutableMap<String, String> getAdditionalMakeVariables() { return additionalMakeVariables; } public PathFragment getLdExecutable() { return ldExecutable; } public final boolean isLLVMCompiler() { // TODO(tmsriram): Checking for "llvm" does not handle all the cases. This // is temporary until the crosstool configuration is modified to add fields that // indicate which flavor of fdo is being used. return toolchainIdentifier.contains("llvm"); } /** * Return the name of the directory (relative to the bin directory) that holds mangled links to * shared libraries. This name is always set to the '{@code _solib_<cpu_archictecture_name>}. */ public String getSolibDirectory() { return solibDirectory; } /** Returns the architecture component of the GNU System Name */ public String getGnuSystemArch() { if (targetSystemName.indexOf('-') == -1) { return targetSystemName; } return targetSystemName.substring(0, targetSystemName.indexOf('-')); } /** Returns the GNU System Name */ public String getTargetGnuSystemName() { return targetSystemName; } public PathFragment getDefaultSysroot() { return defaultSysroot; } /** Returns built-in include directories. */ public ImmutableList<String> getRawBuiltInIncludeDirectories() { return rawBuiltInIncludeDirectories; } }