/** * Returns the linked artifact resulting from a linking of the given type. Consults the feature * configuration to obtain an action_config that provides the artifact. If the feature * configuration provides no artifact, uses a default. * * <p>We cannot assume that the feature configuration contains an action_config for the link * action, because the linux link action depends on hardcoded values in * LinkCommandLine.getRawLinkArgv(), which are applied on the condition that an action_config is * not present. TODO(b/30393154): Assert that the given link action has an action_config. * * @throws RuleErrorException */ private Artifact getLinkedArtifact(LinkTargetType linkTargetType) throws RuleErrorException { Artifact result = null; Artifact linuxDefault = CppHelper.getLinuxLinkedArtifact(ruleContext, linkTargetType); try { String templatedName = features.getArtifactNameForCategory( linkTargetType.getLinkerOutput(), ruleContext, ImmutableMap.<String, String>of()); PathFragment artifactFragment = new PathFragment(ruleContext.getLabel().getName()) .getParentDirectory() .getRelative(templatedName); result = ruleContext.getPackageRelativeArtifact( artifactFragment, ruleContext.getConfiguration().getBinDirectory()); } catch (ExpansionException e) { ruleContext.throwWithRuleError(e.getMessage()); } // If the linked artifact is not the linux default, then a FailAction is generated for the // linux default to satisfy the requirement of the implicit output. // TODO(b/30132703): Remove the implicit outputs of cc_library. if (!result.equals(linuxDefault)) { ruleContext.registerAction( new FailAction( ruleContext.getActionOwner(), ImmutableList.of(linuxDefault), String.format( "the given toolchain supports creation of %s instead of %s", linuxDefault.getExecPathString(), result.getExecPathString()))); } return result; }
/** * Creates an action that converts {@code jarToDex} to a dex file. The output will be stored in * the {@link com.google.devtools.build.lib.actions.Artifact} {@code dxJar}. */ public static void createDexAction( RuleContext ruleContext, Artifact jarToDex, Artifact classesDex, List<String> dexOptions, boolean multidex, Artifact mainDexList) { List<String> args = new ArrayList<>(); args.add("--dex"); // Add --no-locals to coverage builds. Older coverage tools don't correctly preserve local // variable information in stack frame maps that are required since Java 7, so to avoid runtime // errors we just don't add local variable info in the first place. This may no longer be // necessary, however, as long as we use a coverage tool that generates stack frame maps. if (ruleContext.getConfiguration().isCodeCoverageEnabled()) { args.add("--no-locals"); // TODO(bazel-team): Is this still needed? } // Multithreaded dex does not work when using --multi-dex. if (!multidex) { // Multithreaded dex tends to run faster, but only up to about 5 threads (at which point the // law of diminishing returns kicks in). This was determined experimentally, with 5-thread dex // performing about 25% faster than 1-thread dex. args.add("--num-threads=5"); } args.addAll(dexOptions); if (multidex) { args.add("--multi-dex"); if (mainDexList != null) { args.add("--main-dex-list=" + mainDexList.getExecPathString()); } } args.add("--output=" + classesDex.getExecPathString()); args.add(jarToDex.getExecPathString()); SpawnAction.Builder builder = new SpawnAction.Builder() .setExecutable(AndroidSdkProvider.fromRuleContext(ruleContext).getDx()) .addInput(jarToDex) .addOutput(classesDex) .addArguments(args) .setProgressMessage("Converting " + jarToDex.getExecPathString() + " to dex format") .setMnemonic("AndroidDexer") .setResources(ResourceSet.createWithRamCpuIo(4096.0, 5.0, 0.0)); if (mainDexList != null) { builder.addInput(mainDexList); } ruleContext.registerAction(builder.build(ruleContext)); }
/** * Returns a raw link command for the given link invocation, including both command and arguments * (argv). After any further usage-specific processing, this can be passed to {@link * #finalizeWithLinkstampCommands} to give the final command line. * * @return raw link command line. */ public List<String> getRawLinkArgv() { List<String> argv = new ArrayList<>(); switch (linkTargetType) { case EXECUTABLE: argv.add(cppConfiguration.getCppExecutable().getPathString()); argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); argv.addAll(noWholeArchiveFlags); addToolchainFlags(argv); break; case DYNAMIC_LIBRARY: if (interfaceOutput != null) { argv.add(configuration.getShExecutable().getPathString()); argv.add("-c"); argv.add( "build_iface_so=\"$0\"; impl=\"$1\"; iface=\"$2\"; cmd=\"$3\"; shift 3; " + "\"$cmd\" \"$@\" && \"$build_iface_so\" \"$impl\" \"$iface\""); argv.add(interfaceSoBuilder.getExecPathString()); argv.add(output.getExecPathString()); argv.add(interfaceOutput.getExecPathString()); } argv.add(cppConfiguration.getCppExecutable().getPathString()); argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); argv.addAll(noWholeArchiveFlags); addToolchainFlags(argv); break; case STATIC_LIBRARY: case PIC_STATIC_LIBRARY: case ALWAYS_LINK_STATIC_LIBRARY: case ALWAYS_LINK_PIC_STATIC_LIBRARY: // The static library link command follows this template: // ar <cmd> <output_archive> <input_files...> argv.add(cppConfiguration.getArExecutable().getPathString()); argv.addAll( cppConfiguration.getArFlags(cppConfiguration.archiveType() == Link.ArchiveType.THIN)); argv.add(output.getExecPathString()); argv.addAll(featureConfiguration.getCommandLine(actionName, variables)); argv.addAll(noWholeArchiveFlags); break; default: throw new IllegalArgumentException(); } return argv; }
private CustomCommandLine getProtobufCommandLine() { CustomCommandLine.Builder commandLineBuilder = new CustomCommandLine.Builder() .add(attributes.getProtoCompiler().getExecPathString()) .add("--input-file-list") .add(getProtoInputListFile().getExecPathString()) .add("--output-dir") .add(getWorkspaceRelativeOutputDir().getSafePathString()) .add("--force") .add("--proto-root-dir") .add("."); boolean configAdded = false; for (Artifact portableProtoFilter : getPortableProtoFilters()) { String configFlag; if (!configAdded) { configFlag = "--config"; configAdded = true; } else { configFlag = "--extra-filter-config"; } commandLineBuilder.add(configFlag).add(portableProtoFilter.getExecPathString()); } return commandLineBuilder.build(); }
@Test public void testCppLinkActionExtraActionInfoWithSharedLibraries() throws Exception { ConfiguredTarget hello = getConfiguredTarget("//hello:hello"); Artifact sharedObject = FileType.filter(getFilesToBuild(hello), CppFileTypes.SHARED_LIBRARY).iterator().next(); CppLinkAction action = (CppLinkAction) getGeneratingAction(sharedObject); ExtraActionInfo.Builder builder = action.getExtraActionInfo(); ExtraActionInfo info = builder.build(); assertEquals("CppLink", info.getMnemonic()); CppLinkInfo cppLinkInfo = info.getExtension(CppLinkInfo.cppLinkInfo); assertNotNull(cppLinkInfo); Iterable<String> inputs = Artifact.asExecPaths( LinkerInputs.toLibraryArtifacts(action.getLinkCommandLine().getLinkerInputs())); assertThat(cppLinkInfo.getInputFileList()).containsExactlyElementsIn(inputs); assertEquals(action.getPrimaryOutput().getExecPathString(), cppLinkInfo.getOutputFile()); Artifact interfaceOutput = action.getLinkCommandLine().getInterfaceOutput(); assertEquals(interfaceOutput.getExecPathString(), cppLinkInfo.getInterfaceOutputFile()); assertEquals( action.getLinkCommandLine().getLinkTargetType().name(), cppLinkInfo.getLinkTargetType()); assertEquals( action.getLinkCommandLine().getLinkStaticness().name(), cppLinkInfo.getLinkStaticness()); Iterable<String> linkstamps = Artifact.asExecPaths(action.getLinkCommandLine().getLinkstamps().values()); assertThat(cppLinkInfo.getLinkStampList()).containsExactlyElementsIn(linkstamps); Iterable<String> buildInfoHeaderArtifacts = Artifact.asExecPaths(action.getLinkCommandLine().getBuildInfoHeaderArtifacts()); assertThat(cppLinkInfo.getBuildInfoHeaderArtifactList()) .containsExactlyElementsIn(buildInfoHeaderArtifacts); assertThat(cppLinkInfo.getLinkOptList()) .containsExactlyElementsIn(action.getLinkCommandLine().getLinkopts()); }
/** * Creates a generating action for {@code outApk} that builds the APK specified. * * <p>If {@code signingKey} is not null, the apk will be signed with it using the V1 signature * scheme. */ Action[] buildApk(RuleContext ruleContext, Artifact outApk, Artifact signingKey, String message) { SpawnAction.Builder actionBuilder = new SpawnAction.Builder() .setExecutable(AndroidSdkProvider.fromRuleContext(ruleContext).getApkBuilder()) .setProgressMessage(message) .setMnemonic("AndroidApkBuilder") .addOutputArgument(outApk); if (javaResourceZip != null) { actionBuilder.addArgument("-rj").addInputArgument(javaResourceZip); } Artifact nativeSymlinks = nativeLibs.createApkBuilderSymlinks(ruleContext); if (nativeSymlinks != null) { PathFragment nativeSymlinksDir = nativeSymlinks.getExecPath().getParentDirectory(); actionBuilder .addInputManifest(nativeSymlinks, nativeSymlinksDir) .addInput(nativeSymlinks) .addInputs(nativeLibs.getAllNativeLibs()) .addArgument("-nf") // If the native libs are "foo/bar/x86/foo.so", we need to pass "foo/bar" here .addArgument(nativeSymlinksDir.getPathString()); } if (nativeLibs.getName() != null) { actionBuilder .addArgument("-rf") .addArgument(nativeLibs.getName().getExecPath().getParentDirectory().getPathString()) .addInput(nativeLibs.getName()); } if (javaResourceFile != null) { actionBuilder .addArgument("-rf") .addArgument((javaResourceFile.getExecPath().getParentDirectory().getPathString())) .addInput(javaResourceFile); } if (signingKey == null) { actionBuilder.addArgument("-u"); } else { actionBuilder.addArgument("-ks").addArgument(signingKey.getExecPathString()); actionBuilder.addInput(signingKey); } actionBuilder.addArgument("-z").addInputArgument(resourceApk); if (classesDex != null) { actionBuilder .addArgument(classesDex.getFilename().endsWith(".dex") ? "-f" : "-z") .addInputArgument(classesDex); } return actionBuilder.build(ruleContext); }
/** * Adds a source to {@code compilationUnitSources} if it is a compiled file type (including * parsed/preprocessed header) and to {@code privateHeaders} if it is a header. */ private void addSource(Artifact source, Label label) { boolean isHeader = CppFileTypes.CPP_HEADER.matches(source.getExecPath()); boolean isTextualInclude = CppFileTypes.CPP_TEXTUAL_INCLUDE.matches(source.getExecPath()); boolean isCompiledSource = SOURCE_TYPES.matches(source.getExecPathString()); if (isHeader || isTextualInclude) { privateHeaders.add(source); } if (isTextualInclude || !isCompiledSource || (isHeader && !shouldProcessHeaders())) { return; } compilationUnitSources.add(Pair.of(source, label)); }
public static CustomCommandLine.Builder defaultSingleJarCommandLine( Artifact outputJar, String javaMainClass, ImmutableList<String> deployManifestLines, Iterable<Artifact> buildInfoFiles, ImmutableList<Artifact> classpathResources, Iterable<Artifact> runtimeClasspath, boolean includeBuildData, Compression compress, Artifact launcher) { CustomCommandLine.Builder args = CustomCommandLine.builder(); args.addExecPath("--output", outputJar); if (compress == Compression.COMPRESSED) { args.add("--compression"); } args.add("--normalize"); if (javaMainClass != null) { args.add("--main_class"); args.add(javaMainClass); } if (!deployManifestLines.isEmpty()) { args.add("--deploy_manifest_lines"); args.add(deployManifestLines); } if (buildInfoFiles != null) { for (Artifact artifact : buildInfoFiles) { args.addExecPath("--build_info_file", artifact); } } if (!includeBuildData) { args.add("--exclude_build_data"); } if (launcher != null) { args.add("--java_launcher"); args.add(launcher.getExecPathString()); } args.addExecPaths("--classpath_resources", classpathResources); args.addExecPaths("--sources", runtimeClasspath); return args; }
/** * Select .pcm inputs to pass on the command line depending on whether we are in pic or non-pic * mode. */ private Collection<String> getHeaderModulePaths(CppCompileActionBuilder builder, boolean usePic) { Collection<String> result = new LinkedHashSet<>(); NestedSet<Artifact> artifacts = featureConfiguration.isEnabled(CppRuleClasses.HEADER_MODULE_INCLUDES_DEPENDENCIES) ? builder.getContext().getTopLevelHeaderModules(usePic) : builder.getContext().getAdditionalInputs(usePic); for (Artifact artifact : artifacts) { String filename = artifact.getFilename(); if (!filename.endsWith(".pcm")) { continue; } // Depending on whether this specific compile action is pic or non-pic, select the // corresponding header modules. Note that the compilation context might give us both // from targets that are built in both modes. if (usePic == filename.endsWith(".pic.pcm")) { result.add(artifact.getExecPathString()); } } return result; }
protected void createActions( ConfiguredTarget base, RuleContext ruleContext, Iterable<Artifact> protoSources, NestedSet<Artifact> transitiveProtoSources, Iterable<Artifact> headerMappingFiles, Iterable<Artifact> classMappingFiles, J2ObjcSource j2ObjcSource) { String genDir = ruleContext.getConfiguration().getGenfilesDirectory().getExecPathString(); Artifact compiler = ruleContext.getPrerequisiteArtifact("$protoc_darwin", Mode.HOST); Artifact j2objcPlugin = ruleContext.getPrerequisiteArtifact("$j2objc_plugin", Mode.HOST); ruleContext.registerAction( new SpawnAction.Builder() .setMnemonic("TranslatingJ2ObjcProtos") .addInput(compiler) .addInput(j2objcPlugin) .addInputs( ruleContext.getPrerequisiteArtifacts("$protoc_support_darwin", Mode.HOST).list()) .addInputs(protoSources) .addTransitiveInputs(transitiveProtoSources) .addOutputs(j2ObjcSource.getObjcSrcs()) .addOutputs(j2ObjcSource.getObjcHdrs()) .addOutputs(headerMappingFiles) .addOutputs(classMappingFiles) .setExecutable(new PathFragment("/usr/bin/python")) .setCommandLine( new CustomCommandLine.Builder() .add(compiler.getPath().toString()) .add("-w") .add(compiler.getRoot().getPath().toString()) .add("--generate-j2objc") .add("--generator-param=file_dir_mapping") .add("--generator-param=generate_class_mappings") .add("--j2objc-plugin=" + j2objcPlugin.getExecPathString()) .add("--output-dir=" + genDir) .addExecPaths(protoSources) .build()) .setExecutionInfo(ImmutableMap.of(ExecutionRequirements.REQUIRES_DARWIN, "")) .build(ruleContext)); }
/** Returns this target's Xcode build settings. */ private Iterable<XcodeprojBuildSetting> buildSettings() { ImmutableList.Builder<XcodeprojBuildSetting> buildSettings = new ImmutableList.Builder<>(); if (attributes.appIcon() != null) { buildSettings.add( XcodeprojBuildSetting.newBuilder() .setName("ASSETCATALOG_COMPILER_APPICON_NAME") .setValue(attributes.appIcon()) .build()); } if (attributes.launchImage() != null) { buildSettings.add( XcodeprojBuildSetting.newBuilder() .setName("ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME") .setValue(attributes.launchImage()) .build()); } // Convert names to a sequence containing "1" and/or "2" for iPhone and iPad, respectively. ImmutableSet<TargetDeviceFamily> families = bundleSupport.targetDeviceFamilies(); Iterable<Integer> familyIndexes = families.isEmpty() ? ImmutableList.<Integer>of() : UI_DEVICE_FAMILY_VALUES.get(families); buildSettings.add( XcodeprojBuildSetting.newBuilder() .setName("TARGETED_DEVICE_FAMILY") .setValue(Joiner.on(',').join(familyIndexes)) .build()); Artifact entitlements = attributes.entitlements(); if (entitlements != null) { buildSettings.add( XcodeprojBuildSetting.newBuilder() .setName("CODE_SIGN_ENTITLEMENTS") .setValue("$(WORKSPACE_ROOT)/" + entitlements.getExecPathString()) .build()); } return buildSettings.build(); }
private void setupCompileBuildVariables( CppCompileActionBuilder builder, boolean usePic, PathFragment ccRelativeName, PathFragment autoFdoImportPath, Artifact gcnoFile, Artifact dwoFile, Map<String, String> sourceSpecificBuildVariables) { CcToolchainFeatures.Variables.Builder buildVariables = new CcToolchainFeatures.Variables.Builder(); // TODO(bazel-team): Pull out string constants for all build variables. CppCompilationContext builderContext = builder.getContext(); CppModuleMap cppModuleMap = builderContext.getCppModuleMap(); Artifact sourceFile = builder.getSourceFile(); Artifact outputFile = builder.getOutputFile(); String realOutputFilePath; buildVariables.addVariable("source_file", sourceFile.getExecPathString()); buildVariables.addVariable("output_file", outputFile.getExecPathString()); if (builder.getTempOutputFile() != null) { realOutputFilePath = builder.getTempOutputFile().getPathString(); } else { realOutputFilePath = builder.getOutputFile().getExecPathString(); } if (FileType.contains(outputFile, CppFileTypes.ASSEMBLER, CppFileTypes.PIC_ASSEMBLER)) { buildVariables.addVariable("output_assembly_file", realOutputFilePath); } else if (FileType.contains( outputFile, CppFileTypes.PREPROCESSED_C, CppFileTypes.PREPROCESSED_CPP, CppFileTypes.PIC_PREPROCESSED_C, CppFileTypes.PIC_PREPROCESSED_CPP)) { buildVariables.addVariable("output_preprocess_file", realOutputFilePath); } else { buildVariables.addVariable("output_object_file", realOutputFilePath); } DotdFile dotdFile = CppFileTypes.mustProduceDotdFile(sourceFile.getPath().toString()) ? Preconditions.checkNotNull(builder.getDotdFile()) : null; // Set dependency_file to enable <object>.d file generation. if (dotdFile != null) { buildVariables.addVariable("dependency_file", dotdFile.getSafeExecPath().getPathString()); } if (featureConfiguration.isEnabled(CppRuleClasses.MODULE_MAPS) && cppModuleMap != null) { // If the feature is enabled and cppModuleMap is null, we are about to fail during analysis // in any case, but don't crash. buildVariables.addVariable("module_name", cppModuleMap.getName()); buildVariables.addVariable("module_map_file", cppModuleMap.getArtifact().getExecPathString()); CcToolchainFeatures.Variables.ValueSequence.Builder sequence = new CcToolchainFeatures.Variables.ValueSequence.Builder(); for (Artifact artifact : builderContext.getDirectModuleMaps()) { sequence.addValue(artifact.getExecPathString()); } buildVariables.addSequence("dependent_module_map_files", sequence.build()); } if (featureConfiguration.isEnabled(CppRuleClasses.USE_HEADER_MODULES)) { buildVariables.addSequenceVariable("module_files", getHeaderModulePaths(builder, usePic)); } if (featureConfiguration.isEnabled(CppRuleClasses.INCLUDE_PATHS)) { buildVariables.addSequenceVariable( "include_paths", getSafePathStrings(builderContext.getIncludeDirs())); buildVariables.addSequenceVariable( "quote_include_paths", getSafePathStrings(builderContext.getQuoteIncludeDirs())); buildVariables.addSequenceVariable( "system_include_paths", getSafePathStrings(builderContext.getSystemIncludeDirs())); } if (featureConfiguration.isEnabled(CppRuleClasses.PREPROCESSOR_DEFINES)) { String fdoBuildStamp = CppHelper.getFdoBuildStamp(ruleContext); ImmutableList<String> defines; if (fdoBuildStamp != null) { // Stamp FDO builds with FDO subtype string defines = ImmutableList.<String>builder() .addAll(builderContext.getDefines()) .add( CppConfiguration.FDO_STAMP_MACRO + "=\"" + CppHelper.getFdoBuildStamp(ruleContext) + "\"") .build(); } else { defines = builderContext.getDefines(); } buildVariables.addSequenceVariable("preprocessor_defines", defines); } if (usePic) { if (!featureConfiguration.isEnabled(CppRuleClasses.PIC)) { ruleContext.ruleError("PIC compilation is requested but the toolchain does not support it"); } buildVariables.addVariable("pic", ""); } if (ccRelativeName != null) { CppHelper.getFdoSupport(ruleContext) .configureCompilation( builder, buildVariables, ruleContext, ccRelativeName, autoFdoImportPath, usePic, featureConfiguration); } if (gcnoFile != null) { buildVariables.addVariable("gcov_gcno_file", gcnoFile.getExecPathString()); } if (dwoFile != null) { buildVariables.addVariable("per_object_debug_info_file", dwoFile.getExecPathString()); } buildVariables.addAllVariables(CppHelper.getToolchain(ruleContext).getBuildVariables()); buildVariables.addAllVariables(sourceSpecificBuildVariables); for (VariablesExtension extension : variablesExtensions) { extension.addVariables(buildVariables); } CcToolchainFeatures.Variables variables = buildVariables.build(); builder.setVariables(variables); }
/** * Computes, for each C++ source file in {@link #getLinkstamps}, the command necessary to compile * that file such that the output is correctly fed into the link command. * * <p>As these options (as well as all others) are taken into account when computing the action * key, they do not directly contain volatile build information to avoid unnecessary relinking. * Instead this information is passed as an additional header generated by {@link * com.google.devtools.build.lib.rules.cpp.WriteBuildInfoHeaderAction}. * * @param outputPrefix prefix to add before the linkstamp outputs' exec paths * @return a list of shell-escaped compiler commmands, one for each entry in {@link * #getLinkstamps} */ public List<String> getLinkstampCompileCommands(String outputPrefix) { if (linkstamps.isEmpty()) { return ImmutableList.of(); } String compilerCommand = cppConfiguration.getCppExecutable().getPathString(); List<String> commands = Lists.newArrayListWithCapacity(linkstamps.size()); for (Map.Entry<Artifact, Artifact> linkstamp : linkstamps.entrySet()) { List<String> optionList = new ArrayList<>(); // Defines related to the build info are read from generated headers. for (Artifact header : buildInfoHeaderArtifacts) { optionList.add("-include"); optionList.add(header.getExecPathString()); } String labelReplacement = Matcher.quoteReplacement( isSharedNativeLibrary() ? output.getExecPathString() : Label.print(owner.getLabel())); String outputPathReplacement = Matcher.quoteReplacement(output.getExecPathString()); for (String option : linkstampCompileOptions) { optionList.add( option .replaceAll(Pattern.quote("${LABEL}"), labelReplacement) .replaceAll(Pattern.quote("${OUTPUT_PATH}"), outputPathReplacement)); } optionList.add("-DGPLATFORM=\"" + cppConfiguration + "\""); // Needed to find headers included from linkstamps. optionList.add("-I."); // Add sysroot. PathFragment sysroot = cppConfiguration.getSysroot(); if (sysroot != null) { optionList.add("--sysroot=" + sysroot.getPathString()); } // Add toolchain compiler options. optionList.addAll(cppConfiguration.getCompilerOptions(features)); optionList.addAll(cppConfiguration.getCOptions()); optionList.addAll(cppConfiguration.getUnfilteredCompilerOptions(features)); if (CppFileTypes.CPP_SOURCE.matches(linkstamp.getKey().getExecPath())) { optionList.addAll(cppConfiguration.getCxxOptions(features)); } // For dynamic libraries, produce position independent code. if (linkTargetType == LinkTargetType.DYNAMIC_LIBRARY && cppConfiguration.toolchainNeedsPic()) { optionList.add("-fPIC"); } // Stamp FDO builds with FDO subtype string if (fdoBuildStamp != null) { optionList.add("-D" + CppConfiguration.FDO_STAMP_MACRO + "=\"" + fdoBuildStamp + "\""); } // Add the compilation target. optionList.add("-c"); optionList.add(linkstamp.getKey().getExecPathString()); // Assemble the final command, exempting outputPrefix from shell escaping. commands.add( compilerCommand + " " + ShellEscaper.escapeJoinAll(optionList) + " -o " + outputPrefix + ShellEscaper.escapeString(linkstamp.getValue().getExecPathString())); } return commands; }
/** * {@inheritDoc} * * <p>Creates a target, including registering actions, just as {@link #create(RuleContext)} does. * The difference between {@link #create(RuleContext)} and this method is that this method does * only what is needed to support tests on the environment besides generate the Xcodeproj file and * build the app and test {@code .ipa}s. The {@link #create(RuleContext)} method delegates to this * method. */ @Override public final ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException, RuleErrorException { ProtobufSupport protoSupport = new ProtobufSupport(ruleContext).registerGenerationActions().registerCompilationActions(); Optional<ObjcProvider> protosObjcProvider = protoSupport.getObjcProvider(); Optional<XcodeProvider> protosXcodeProvider = protoSupport.getXcodeProvider(); ObjcCommon common = common(ruleContext, protosObjcProvider); if (!common.getCompilationArtifacts().get().getArchive().isPresent()) { ruleContext.ruleError(REQUIRES_SOURCE_ERROR); } if (!ruleContext.getFragment(AppleConfiguration.class).getIosMultiCpus().isEmpty()) { ruleContext.ruleError(NO_MULTI_CPUS_ERROR); } XcodeProvider.Builder xcodeProviderBuilder = new XcodeProvider.Builder().addPropagatedDependencies(protosXcodeProvider.asSet()); NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.stableOrder(); addResourceFilesToBuild(ruleContext, common.getObjcProvider(), filesToBuild); XcodeProductType productType = getProductType(ruleContext); ExtraLinkArgs extraLinkArgs; Iterable<Artifact> extraLinkInputs; String bundleFormat; if (!isXcTest(ruleContext)) { extraLinkArgs = new ExtraLinkArgs(); extraLinkInputs = ImmutableList.of(); bundleFormat = ReleaseBundlingSupport.APP_BUNDLE_DIR_FORMAT; } else { XcodeProvider appIpaXcodeProvider = ruleContext.getPrerequisite(XCTEST_APP_ATTR, Mode.TARGET, XcodeProvider.class); xcodeProviderBuilder.setTestHost(appIpaXcodeProvider).setProductType(productType); XcTestAppProvider testApp = xcTestAppProvider(ruleContext); Artifact bundleLoader = testApp.getBundleLoader(); // -bundle causes this binary to be linked as a bundle and not require an entry point // (i.e. main()) // -bundle_loader causes the code in this test to have access to the symbols in the test rig, // or more specifically, the flag causes ld to consider the given binary when checking for // missing symbols. // -rpath @loader_path/Frameworks allows test bundles to load dylibs from the app's // Frameworks directory. extraLinkArgs = new ExtraLinkArgs( "-bundle", "-bundle_loader", bundleLoader.getExecPathString(), "-Xlinker", "-rpath", "-Xlinker", "@loader_path/Frameworks"); extraLinkInputs = ImmutableList.of(bundleLoader); bundleFormat = ReleaseBundlingSupport.XCTEST_BUNDLE_DIR_FORMAT; filesToBuild.add(testApp.getIpa()); } J2ObjcMappingFileProvider j2ObjcMappingFileProvider = J2ObjcMappingFileProvider.union( ruleContext.getPrerequisites("deps", Mode.TARGET, J2ObjcMappingFileProvider.class)); J2ObjcEntryClassProvider j2ObjcEntryClassProvider = new J2ObjcEntryClassProvider.Builder() .addTransitive( ruleContext.getPrerequisites("deps", Mode.TARGET, J2ObjcEntryClassProvider.class)) .build(); new CompilationSupport(ruleContext) .registerLinkActions( common.getObjcProvider(), j2ObjcMappingFileProvider, j2ObjcEntryClassProvider, extraLinkArgs, extraLinkInputs, DsymOutputType.TEST) .registerCompileAndArchiveActions(common) .registerFullyLinkAction( common.getObjcProvider(), ruleContext.getImplicitOutputArtifact(CompilationSupport.FULLY_LINKED_LIB)) .addXcodeSettings(xcodeProviderBuilder, common) .validateAttributes(); AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); new ReleaseBundlingSupport( ruleContext, common.getObjcProvider(), LinkedBinary.LOCAL_AND_DEPENDENCIES, bundleFormat, appleConfiguration.getMinimumOsForPlatformType(PlatformType.IOS), appleConfiguration.getMultiArchPlatform(PlatformType.IOS)) .registerActions(DsymOutputType.TEST) .addXcodeSettings(xcodeProviderBuilder) .addFilesToBuild(filesToBuild, Optional.of(DsymOutputType.TEST)) .validateResources() .validateAttributes(); new ResourceSupport(ruleContext).validateAttributes().addXcodeSettings(xcodeProviderBuilder); new XcodeSupport(ruleContext) .addXcodeSettings(xcodeProviderBuilder, common.getObjcProvider(), productType) .addDependencies(xcodeProviderBuilder, new Attribute("bundles", Mode.TARGET)) .addDependencies(xcodeProviderBuilder, new Attribute("deps", Mode.TARGET)) .addNonPropagatedDependencies( xcodeProviderBuilder, new Attribute("non_propagated_deps", Mode.TARGET)) .addFilesToBuild(filesToBuild) .registerActions(xcodeProviderBuilder.build()); XcodeProvider xcodeProvider = xcodeProviderBuilder.build(); NestedSet<Artifact> filesToBuildSet = filesToBuild.build(); Runfiles.Builder runfilesBuilder = new Runfiles.Builder( ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles()) .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES); NestedSetBuilder<Artifact> filesToBuildBuilder = NestedSetBuilder.<Artifact>stableOrder().addTransitive(filesToBuildSet); InstrumentedFilesProvider instrumentedFilesProvider = new CompilationSupport(ruleContext).getInstrumentedFilesProvider(common); TestSupport testSupport = new TestSupport(ruleContext) .registerTestRunnerActions() .addRunfiles(runfilesBuilder, instrumentedFilesProvider) .addFilesToBuild(filesToBuildBuilder); Artifact executable = testSupport.generatedTestScript(); Runfiles runfiles = runfilesBuilder.build(); RunfilesSupport runfilesSupport = RunfilesSupport.withExecutable(ruleContext, runfiles, executable); ImmutableMap.Builder<String, String> execInfoMapBuilder = new ImmutableMap.Builder<>(); execInfoMapBuilder.put(ExecutionRequirements.REQUIRES_DARWIN, ""); if (ruleContext.getFragment(ObjcConfiguration.class).runMemleaks()) { execInfoMapBuilder.put("nosandbox", ""); } return new RuleConfiguredTargetBuilder(ruleContext) .setFilesToBuild(filesToBuildBuilder.build()) .addProvider(xcodeProvider) .addProvider(RunfilesProvider.simple(runfiles)) .addProvider(new ExecutionInfoProvider(execInfoMapBuilder.build())) .addProvider(InstrumentedFilesProvider.class, instrumentedFilesProvider) .addProviders(testSupport.getExtraProviders()) .setRunfilesSupport(runfilesSupport, executable) .build(); }
private String extractPlistCommand(Artifact provisioningProfile) { return "security cms -D -i " + ShellUtils.shellEscape(provisioningProfile.getExecPathString()); }
private TargetControl targetControl() { String buildFilePath = label.getPackageFragment().getSafePathString() + "/BUILD"; NestedSet<String> userHeaderSearchPaths = NestedSetBuilder.<String>linkOrder() .addTransitive(propagatedUserHeaderSearchPaths) .addTransitive(nonPropagatedUserHeaderSearchPaths) .build(); NestedSet<String> headerSearchPaths = NestedSetBuilder.<String>linkOrder() .addTransitive(propagatedHeaderSearchPaths) .addTransitive(nonPropagatedHeaderSearchPaths) .build(); // TODO(bazel-team): Add provisioning profile information when Xcodegen supports it. TargetControl.Builder targetControl = TargetControl.newBuilder() .setName(label.getName()) .setLabel(xcodeTargetName(label)) .setProductType(productType.getIdentifier()) .addSupportFile(buildFilePath) .addAllImportedLibrary(Artifact.toExecPaths(objcProvider.get(IMPORTED_LIBRARY))) .addAllImportedLibrary(Artifact.toExecPaths(ccLibraries(objcProvider))) .addAllUserHeaderSearchPath(userHeaderSearchPaths) .addAllHeaderSearchPath(headerSearchPaths) .addAllSupportFile(Artifact.toExecPaths(headers)) .addAllCopt(compilationModeCopts) .addAllCopt(CompilationSupport.DEFAULT_COMPILER_FLAGS) .addAllCopt(Interspersing.prependEach("-D", objcProvider.get(DEFINE))) .addAllCopt(copts) .addAllLinkopt( Interspersing.beforeEach("-force_load", objcProvider.get(FORCE_LOAD_FOR_XCODEGEN))) .addAllLinkopt(CompilationSupport.DEFAULT_LINKER_FLAGS) .addAllLinkopt( Interspersing.beforeEach( "-weak_framework", SdkFramework.names(objcProvider.get(WEAK_SDK_FRAMEWORK)))) .addAllBuildSetting(xcodeprojBuildSettings) .addAllBuildSetting(AppleToolchain.defaultWarningsForXcode()) .addAllSdkFramework(SdkFramework.names(objcProvider.get(SDK_FRAMEWORK))) .addAllFramework(PathFragment.safePathStrings(objcProvider.get(FRAMEWORK_DIR))) .addAllFrameworkSearchPathOnly( PathFragment.safePathStrings(objcProvider.get(FRAMEWORK_SEARCH_PATH_ONLY))) .addAllXcassetsDir(PathFragment.safePathStrings(objcProvider.get(XCASSETS_DIR))) .addAllXcdatamodel( PathFragment.safePathStrings( Xcdatamodels.datamodelDirs(objcProvider.get(XCDATAMODEL)))) .addAllBundleImport(PathFragment.safePathStrings(objcProvider.get(BUNDLE_IMPORT_DIR))) .addAllSdkDylib(objcProvider.get(SDK_DYLIB)) .addAllGeneralResourceFile( Artifact.toExecPaths(objcProvider.get(GENERAL_RESOURCE_FILE))) .addAllGeneralResourceFile( PathFragment.safePathStrings(objcProvider.get(GENERAL_RESOURCE_DIR))); if (CAN_LINK_PRODUCT_TYPES.contains(productType)) { // For builds with --ios_multi_cpus set, we may have several copies of some XCodeProviders // in the dependencies (one per cpu architecture). We deduplicate the corresponding // xcode target names with a LinkedHashSet before adding to the TargetControl. Set<String> jreTargetNames = new HashSet<>(); for (XcodeProvider jreDependency : jreDependencies) { jreTargetNames.add(jreDependency.dependencyXcodeTargetName()); } Set<DependencyControl> dependencySet = new LinkedHashSet<>(); Set<DependencyControl> jreDependencySet = new LinkedHashSet<>(); for (XcodeProvider dependency : propagatedDependencies) { // Only add a library target to a binary's dependencies if it has source files to compile // and it is not from the "non_propagated_deps" attribute. Xcode cannot build targets // without a source file in the PBXSourceFilesBuildPhase, so if such a target is present in // the control file, it is only to get Xcodegen to put headers and resources not used by the // final binary in the Project Navigator. // // The exceptions to this rule are objc_bundle_library and ios_extension targets. Bundles // are generally used for resources and can lack a PBXSourceFilesBuildPhase in the project // file and still be considered valid by Xcode. // // ios_extension targets are an exception because they have no CompilationArtifact object // but do have a dummy source file to make Xcode happy. boolean hasSources = dependency.compilationArtifacts.isPresent() && dependency.compilationArtifacts.get().getArchive().isPresent(); if (hasSources || (dependency.productType == XcodeProductType.BUNDLE || (dependency.productType == XcodeProductType.WATCH_OS1_APPLICATION))) { String dependencyXcodeTargetName = dependency.dependencyXcodeTargetName(); Set<DependencyControl> set = jreTargetNames.contains(dependencyXcodeTargetName) ? jreDependencySet : dependencySet; set.add(DependencyControl.newBuilder().setTargetLabel(dependencyXcodeTargetName).build()); } } for (DependencyControl dependencyControl : dependencySet) { targetControl.addDependency(dependencyControl); } // Make sure that JRE dependencies are ordered after other propagated dependencies. for (DependencyControl dependencyControl : jreDependencySet) { targetControl.addDependency(dependencyControl); } } for (XcodeProvider justTestHost : testHost.asSet()) { targetControl.addDependency( DependencyControl.newBuilder() .setTargetLabel(xcodeTargetName(justTestHost.label)) .setTestHost(true) .build()); } for (XcodeProvider extension : extensions) { targetControl.addDependency( DependencyControl.newBuilder().setTargetLabel(xcodeTargetName(extension.label)).build()); } if (bundleInfoplist.isPresent()) { targetControl.setInfoplist(bundleInfoplist.get().getExecPathString()); } for (CompilationArtifacts artifacts : compilationArtifacts.asSet()) { targetControl .addAllSourceFile(Artifact.toExecPaths(artifacts.getSrcs())) .addAllSupportFile(Artifact.toExecPaths(artifacts.getAdditionalHdrs())) .addAllSupportFile(Artifact.toExecPaths(artifacts.getPrivateHdrs())) .addAllNonArcSourceFile(Artifact.toExecPaths(artifacts.getNonArcSrcs())); for (Artifact pchFile : artifacts.getPchFile().asSet()) { targetControl .setPchPath(pchFile.getExecPathString()) .addSupportFile(pchFile.getExecPathString()); } } for (Artifact artifact : additionalSources) { targetControl.addSourceFile(artifact.getExecPathString()); } if (objcProvider.is(Flag.USES_CPP)) { targetControl.addSdkDylib("libc++"); } return targetControl.build(); }