| // Copyright 2015 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.objc; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode.TARGET; |
| import static com.google.devtools.build.lib.packages.Attribute.attr; |
| import static com.google.devtools.build.lib.packages.BuildType.LABEL; |
| import static java.nio.charset.StandardCharsets.ISO_8859_1; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; |
| import com.google.devtools.build.lib.actions.ParamFileInfo; |
| import com.google.devtools.build.lib.actions.ParameterFile; |
| import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType; |
| import com.google.devtools.build.lib.analysis.ConfiguredAspect; |
| import com.google.devtools.build.lib.analysis.ConfiguredAspectFactory; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; |
| import com.google.devtools.build.lib.analysis.TransitiveInfoCollection; |
| import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; |
| import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.VectorArg; |
| import com.google.devtools.build.lib.analysis.actions.SpawnAction; |
| import com.google.devtools.build.lib.analysis.config.ConfigAwareAspectBuilder; |
| import com.google.devtools.build.lib.analysis.config.HostTransition; |
| import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.packages.AspectDefinition; |
| import com.google.devtools.build.lib.packages.AspectParameters; |
| import com.google.devtools.build.lib.packages.Attribute.LabelLateBoundDefault; |
| import com.google.devtools.build.lib.packages.Attribute.LateBoundDefault.Resolver; |
| import com.google.devtools.build.lib.packages.BuildType; |
| import com.google.devtools.build.lib.packages.NativeAspectClass; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; |
| import com.google.devtools.build.lib.rules.apple.AppleConfiguration; |
| import com.google.devtools.build.lib.rules.apple.AppleToolchain; |
| import com.google.devtools.build.lib.rules.apple.XcodeConfigRule; |
| import com.google.devtools.build.lib.rules.cpp.CcToolchainProvider; |
| import com.google.devtools.build.lib.rules.cpp.CppConfiguration; |
| import com.google.devtools.build.lib.rules.cpp.CppHelper; |
| import com.google.devtools.build.lib.rules.cpp.CppRuleClasses; |
| import com.google.devtools.build.lib.rules.java.JavaCommon; |
| import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; |
| import com.google.devtools.build.lib.rules.java.JavaConfiguration; |
| import com.google.devtools.build.lib.rules.java.JavaGenJarsProvider; |
| import com.google.devtools.build.lib.rules.java.JavaInfo; |
| import com.google.devtools.build.lib.rules.java.JavaRuntimeInfo; |
| import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider; |
| import com.google.devtools.build.lib.rules.objc.CompilationSupport.ExtraCompileArgs; |
| import com.google.devtools.build.lib.rules.objc.J2ObjcSource.SourceType; |
| import com.google.devtools.build.lib.rules.proto.ProtoCommon; |
| import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder; |
| import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder.Exports; |
| import com.google.devtools.build.lib.rules.proto.ProtoCompileActionBuilder.Services; |
| import com.google.devtools.build.lib.rules.proto.ProtoConfiguration; |
| import com.google.devtools.build.lib.rules.proto.ProtoInfo; |
| import com.google.devtools.build.lib.rules.proto.ProtoLangToolchainProvider; |
| import com.google.devtools.build.lib.rules.proto.ProtoSourceFileBlacklist; |
| import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData; |
| import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; |
| import com.google.devtools.build.lib.util.FileType; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.stream.Collectors; |
| |
| /** J2ObjC transpilation aspect for Java and proto rules. */ |
| public class J2ObjcAspect extends NativeAspectClass implements ConfiguredAspectFactory { |
| public static final String NAME = "J2ObjcAspect"; |
| |
| private static final ExtraCompileArgs EXTRA_COMPILE_ARGS = new ExtraCompileArgs( |
| "-fno-strict-overflow"); |
| |
| private static LabelLateBoundDefault<ProtoConfiguration> getProtoToolchainLabel( |
| String defaultValue) { |
| return LabelLateBoundDefault.fromTargetConfiguration( |
| ProtoConfiguration.class, |
| Label.parseAbsoluteUnchecked(defaultValue), |
| (Resolver<ProtoConfiguration, Label> & Serializable) |
| (rule, attributes, protoConfig) -> protoConfig.protoToolchainForJ2objc()); |
| } |
| |
| private static final ImmutableList<Attribute> JAVA_DEPENDENT_ATTRIBUTES = |
| ImmutableList.of( |
| new Attribute("$jre_lib", Mode.TARGET), |
| new Attribute("deps", Mode.TARGET), |
| new Attribute("exports", Mode.TARGET), |
| new Attribute("runtime_deps", Mode.TARGET)); |
| |
| private static final ImmutableList<Attribute> PROTO_DEPENDENT_ATTRIBUTES = |
| ImmutableList.of(new Attribute("deps", Mode.TARGET)); |
| |
| private static final String J2OBJC_PROTO_TOOLCHAIN_ATTR = ":j2objc_proto_toolchain"; |
| |
| @AutoCodec @AutoCodec.VisibleForSerialization |
| static final LabelLateBoundDefault<?> DEAD_CODE_REPORT = |
| LabelLateBoundDefault.fromTargetConfiguration( |
| J2ObjcConfiguration.class, |
| null, |
| (rule, attributes, j2objcConfig) -> j2objcConfig.deadCodeReport()); |
| |
| private final String toolsRepository; |
| private final Label ccToolchainType; |
| private final LabelLateBoundDefault<CppConfiguration> ccToolchain; |
| |
| public J2ObjcAspect(RuleDefinitionEnvironment env) { |
| this.toolsRepository = checkNotNull(env.getToolsRepository()); |
| this.ccToolchainType = CppRuleClasses.ccToolchainTypeAttribute(env); |
| this.ccToolchain = CppRuleClasses.ccToolchainAttribute(env); |
| } |
| |
| /** Returns whether this aspect allows proto services to be generated from this proto rule */ |
| protected boolean shouldAllowProtoServices(RuleContext ruleContext) { |
| return true; |
| } |
| |
| @Override |
| public AspectDefinition getDefinition(AspectParameters aspectParameters) { |
| return ConfigAwareAspectBuilder.of(new AspectDefinition.Builder(this)) |
| .requiresHostConfigurationFragments(JavaConfiguration.class) |
| .originalBuilder() |
| .propagateAlongAttribute("deps") |
| .propagateAlongAttribute("exports") |
| .propagateAlongAttribute("runtime_deps") |
| .requireSkylarkProviders(SkylarkProviderIdentifier.forKey(JavaInfo.PROVIDER.getKey())) |
| .requireSkylarkProviders(ProtoInfo.PROVIDER.id()) |
| .requiresConfigurationFragments( |
| AppleConfiguration.class, |
| CppConfiguration.class, |
| J2ObjcConfiguration.class, |
| ObjcConfiguration.class, |
| ProtoConfiguration.class) |
| .addRequiredToolchains(ccToolchainType) |
| .add( |
| attr("$grep_includes", LABEL) |
| .cfg(HostTransition.createFactory()) |
| .value(Label.parseAbsoluteUnchecked(toolsRepository + "//tools/cpp:grep-includes"))) |
| .add( |
| attr("$j2objc", LABEL) |
| .cfg(HostTransition.createFactory()) |
| .exec() |
| .value( |
| Label.parseAbsoluteUnchecked( |
| toolsRepository + "//tools/j2objc:j2objc_deploy.jar"))) |
| .add( |
| attr("$j2objc_wrapper", LABEL) |
| .allowedFileTypes(FileType.of(".py")) |
| .cfg(HostTransition.createFactory()) |
| .exec() |
| .singleArtifact() |
| .value( |
| Label.parseAbsoluteUnchecked( |
| toolsRepository + "//tools/j2objc:j2objc_wrapper"))) |
| .add( |
| attr("$j2objc_header_map", LABEL) |
| .allowedFileTypes(FileType.of(".py")) |
| .cfg(HostTransition.createFactory()) |
| .exec() |
| .singleArtifact() |
| .value( |
| Label.parseAbsoluteUnchecked( |
| toolsRepository + "//tools/j2objc:j2objc_header_map"))) |
| .add( |
| attr("$jre_emul_jar", LABEL) |
| .cfg(HostTransition.createFactory()) |
| .value( |
| Label.parseAbsoluteUnchecked( |
| toolsRepository + "//third_party/java/j2objc:jre_emul.jar"))) |
| .add( |
| attr("$jre_emul_module", LABEL) |
| .cfg(HostTransition.createFactory()) |
| .value( |
| Label.parseAbsoluteUnchecked( |
| toolsRepository + "//third_party/java/j2objc:jre_emul_module"))) |
| .add( |
| attr(":dead_code_report", LABEL) |
| .cfg(HostTransition.createFactory()) |
| .value(DEAD_CODE_REPORT)) |
| .add( |
| attr("$jre_lib", LABEL) |
| .value( |
| Label.parseAbsoluteUnchecked( |
| toolsRepository + "//third_party/java/j2objc:jre_core_lib"))) |
| .add( |
| attr("$xcrunwrapper", LABEL) |
| .cfg(HostTransition.createFactory()) |
| .exec() |
| .value(Label.parseAbsoluteUnchecked(toolsRepository + "//tools/objc:xcrunwrapper"))) |
| .add( |
| attr(ObjcRuleClasses.LIBTOOL_ATTRIBUTE, LABEL) |
| .cfg(HostTransition.createFactory()) |
| .exec() |
| .value(Label.parseAbsoluteUnchecked(toolsRepository + "//tools/objc:libtool"))) |
| .add( |
| attr(XcodeConfigRule.XCODE_CONFIG_ATTR_NAME, LABEL) |
| .allowedRuleClasses("xcode_config") |
| .checkConstraints() |
| .direct_compile_time_input() |
| .value(AppleToolchain.getXcodeConfigLabel(toolsRepository))) |
| .add( |
| attr("$zipper", LABEL) |
| .cfg(HostTransition.createFactory()) |
| .exec() |
| .value(Label.parseAbsoluteUnchecked(toolsRepository + "//tools/zip:zipper"))) |
| .add( |
| attr(J2OBJC_PROTO_TOOLCHAIN_ATTR, LABEL) |
| .legacyAllowAnyFileType() |
| .value( |
| getProtoToolchainLabel( |
| toolsRepository + "//tools/j2objc:j2objc_proto_toolchain"))) |
| .add(attr(":j2objc_cc_toolchain", LABEL).value(ccToolchain)) |
| .build(); |
| } |
| |
| @Override |
| public ConfiguredAspect create( |
| ConfiguredTargetAndData ctadBase, |
| RuleContext ruleContext, |
| AspectParameters parameters, |
| String toolsRepository) |
| throws InterruptedException, ActionConflictException { |
| ConfiguredTarget base = ctadBase.getConfiguredTarget(); |
| if (isProtoRule(base)) { |
| return proto(base, ruleContext, parameters); |
| } else { |
| return java(base, ruleContext, parameters); |
| } |
| } |
| |
| private ConfiguredAspect buildAspect( |
| ConfiguredTarget base, |
| RuleContext ruleContext, |
| AspectParameters parameters, |
| J2ObjcSource j2ObjcSource, |
| J2ObjcMappingFileProvider directJ2ObjcMappingFileProvider, |
| List<Attribute> depAttributes, |
| List<TransitiveInfoCollection> otherDeps) |
| throws InterruptedException, ActionConflictException { |
| ConfiguredAspect.Builder builder = new ConfiguredAspect.Builder(this, parameters, ruleContext); |
| ObjcCommon common; |
| |
| if (!j2ObjcSource.getObjcSrcs().isEmpty()) { |
| common = |
| common( |
| ruleContext, |
| j2ObjcSource.getObjcSrcs(), |
| j2ObjcSource.getObjcHdrs(), |
| j2ObjcSource.getHeaderSearchPaths(), |
| depAttributes, |
| otherDeps); |
| |
| try { |
| CcToolchainProvider ccToolchain = |
| CppHelper.getToolchain(ruleContext, ":j2objc_cc_toolchain"); |
| CompilationSupport compilationSupport = |
| new CompilationSupport.Builder() |
| .setRuleContext(ruleContext) |
| .setToolchainProvider(ccToolchain) |
| .setIntermediateArtifacts(ObjcRuleClasses.j2objcIntermediateArtifacts(ruleContext)) |
| .doNotUsePch() |
| .build(); |
| |
| compilationSupport |
| .registerCompileAndArchiveActions( |
| common, EXTRA_COMPILE_ARGS, ImmutableList.<PathFragment>of()) |
| .registerFullyLinkAction( |
| common.getObjcProvider(), |
| ruleContext.getImplicitOutputArtifact(CompilationSupport.FULLY_LINKED_LIB)); |
| } catch (RuleErrorException e) { |
| ruleContext.ruleError(e.getMessage()); |
| } |
| } else { |
| common = |
| common( |
| ruleContext, |
| ImmutableList.<Artifact>of(), |
| ImmutableList.<Artifact>of(), |
| ImmutableList.<PathFragment>of(), |
| depAttributes, |
| otherDeps); |
| } |
| |
| return builder |
| .addProvider( |
| exportedJ2ObjcMappingFileProvider(base, ruleContext, directJ2ObjcMappingFileProvider)) |
| .addNativeDeclaredProvider(common.getObjcProvider()) |
| .build(); |
| } |
| |
| private ConfiguredAspect java( |
| ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) |
| throws InterruptedException, ActionConflictException { |
| JavaCompilationArgsProvider compilationArgsProvider = |
| JavaInfo.getProvider(JavaCompilationArgsProvider.class, base); |
| JavaSourceInfoProvider sourceInfoProvider = |
| JavaInfo.getProvider(JavaSourceInfoProvider.class, base); |
| JavaGenJarsProvider genJarProvider = JavaInfo.getProvider(JavaGenJarsProvider.class, base); |
| ImmutableSet.Builder<Artifact> javaSourceFilesBuilder = ImmutableSet.builder(); |
| ImmutableSet.Builder<Artifact> javaSourceJarsBuilder = ImmutableSet.builder(); |
| if (sourceInfoProvider != null) { |
| javaSourceFilesBuilder.addAll(sourceInfoProvider.getSourceFiles()); |
| javaSourceJarsBuilder |
| .addAll(sourceInfoProvider.getSourceJars()) |
| .addAll(sourceInfoProvider.getSourceJarsForJarFiles()); |
| } |
| |
| if (genJarProvider != null && genJarProvider.getGenSourceJar() != null) { |
| javaSourceJarsBuilder.add(genJarProvider.getGenSourceJar()); |
| } |
| |
| ImmutableList<Artifact> javaSourceFiles = javaSourceFilesBuilder.build().asList(); |
| ImmutableList<Artifact> javaSourceJars = javaSourceJarsBuilder.build().asList(); |
| J2ObjcSource j2ObjcSource = javaJ2ObjcSource(ruleContext, javaSourceFiles, javaSourceJars); |
| J2ObjcMappingFileProvider depJ2ObjcMappingFileProvider = |
| depJ2ObjcMappingFileProvider(ruleContext); |
| |
| J2ObjcMappingFileProvider directJ2ObjcMappingFileProvider; |
| if (j2ObjcSource.getObjcSrcs().isEmpty()) { |
| directJ2ObjcMappingFileProvider = new J2ObjcMappingFileProvider.Builder().build(); |
| } else { |
| directJ2ObjcMappingFileProvider = |
| createJ2ObjcTranspilationAction( |
| ruleContext, |
| javaSourceFiles, |
| javaSourceJars, |
| depJ2ObjcMappingFileProvider, |
| compilationArgsProvider, |
| j2ObjcSource); |
| } |
| return buildAspect( |
| base, |
| ruleContext, |
| parameters, |
| j2ObjcSource, |
| directJ2ObjcMappingFileProvider, |
| JAVA_DEPENDENT_ATTRIBUTES, |
| ImmutableList.of()); |
| } |
| |
| private ConfiguredAspect proto( |
| ConfiguredTarget base, RuleContext ruleContext, AspectParameters parameters) |
| throws InterruptedException, ActionConflictException { |
| ProtoInfo protoInfo = base.get(ProtoInfo.PROVIDER); |
| ImmutableList<Artifact> protoSources = protoInfo.getDirectProtoSources(); |
| |
| ProtoLangToolchainProvider protoToolchain = |
| ruleContext.getPrerequisite( |
| J2OBJC_PROTO_TOOLCHAIN_ATTR, TARGET, ProtoLangToolchainProvider.class); |
| // Avoid pulling in any generated files from blacklisted protos. |
| ProtoSourceFileBlacklist protoBlacklist = |
| new ProtoSourceFileBlacklist(ruleContext, protoToolchain.blacklistedProtos()); |
| |
| ImmutableList<Artifact> filteredProtoSources = |
| ImmutableList.copyOf(protoBlacklist.filter(protoSources)); |
| J2ObjcSource j2ObjcSource = protoJ2ObjcSource(ruleContext, filteredProtoSources); |
| |
| J2ObjcMappingFileProvider directJ2ObjcMappingFileProvider; |
| if (j2ObjcSource.getObjcSrcs().isEmpty()) { |
| directJ2ObjcMappingFileProvider = new J2ObjcMappingFileProvider.Builder().build(); |
| } else { |
| directJ2ObjcMappingFileProvider = |
| createJ2ObjcProtoCompileActions( |
| base, protoToolchain, ruleContext, filteredProtoSources, j2ObjcSource); |
| } |
| |
| return buildAspect( |
| base, |
| ruleContext, |
| parameters, |
| j2ObjcSource, |
| directJ2ObjcMappingFileProvider, |
| PROTO_DEPENDENT_ATTRIBUTES, |
| ImmutableList.of(protoToolchain.runtime())); |
| } |
| |
| private static J2ObjcMappingFileProvider exportedJ2ObjcMappingFileProvider( |
| ConfiguredTarget base, |
| RuleContext ruleContext, |
| J2ObjcMappingFileProvider directJ2ObjcMappingFileProvider) { |
| J2ObjcMappingFileProvider depJ2ObjcMappingFileProvider = |
| depJ2ObjcMappingFileProvider(ruleContext); |
| |
| NestedSetBuilder<Artifact> exportedHeaderMappingFiles = |
| NestedSetBuilder.<Artifact>stableOrder() |
| .addTransitive(directJ2ObjcMappingFileProvider.getHeaderMappingFiles()); |
| |
| NestedSetBuilder<Artifact> exportedClassMappingFiles = |
| NestedSetBuilder.<Artifact>stableOrder() |
| .addTransitive(directJ2ObjcMappingFileProvider.getClassMappingFiles()) |
| .addTransitive(depJ2ObjcMappingFileProvider.getClassMappingFiles()); |
| |
| NestedSetBuilder<Artifact> exportedDependencyMappingFiles = |
| NestedSetBuilder.<Artifact>stableOrder() |
| .addTransitive(directJ2ObjcMappingFileProvider.getDependencyMappingFiles()) |
| .addTransitive(depJ2ObjcMappingFileProvider.getDependencyMappingFiles()); |
| |
| NestedSetBuilder<Artifact> archiveSourceMappingFiles = |
| NestedSetBuilder.<Artifact>stableOrder() |
| .addTransitive(directJ2ObjcMappingFileProvider.getArchiveSourceMappingFiles()) |
| .addTransitive(depJ2ObjcMappingFileProvider.getArchiveSourceMappingFiles()); |
| |
| // J2ObjC merges all transitive input header mapping files into one header mapping file, |
| // so we only need to re-export other dependent output header mapping files in proto rules and |
| // rules where J2ObjC is not run (e.g., no sources). |
| // We also add the transitive header mapping files if experimental J2ObjC header mapping is |
| // turned on. The experimental support does not merge transitive input header mapping files. |
| boolean experimentalJ2ObjcHeaderMap = |
| ruleContext.getFragment(J2ObjcConfiguration.class).experimentalJ2ObjcHeaderMap(); |
| if (isProtoRule(base) || exportedHeaderMappingFiles.isEmpty() || experimentalJ2ObjcHeaderMap) { |
| exportedHeaderMappingFiles.addTransitive( |
| depJ2ObjcMappingFileProvider.getHeaderMappingFiles()); |
| } |
| |
| return new J2ObjcMappingFileProvider( |
| exportedHeaderMappingFiles.build(), |
| exportedClassMappingFiles.build(), |
| exportedDependencyMappingFiles.build(), |
| archiveSourceMappingFiles.build()); |
| } |
| |
| private static J2ObjcMappingFileProvider depJ2ObjcMappingFileProvider(RuleContext ruleContext) { |
| NestedSetBuilder<Artifact> depsHeaderMappingsBuilder = NestedSetBuilder.stableOrder(); |
| NestedSetBuilder<Artifact> depsClassMappingsBuilder = NestedSetBuilder.stableOrder(); |
| NestedSetBuilder<Artifact> depsDependencyMappingsBuilder = NestedSetBuilder.stableOrder(); |
| NestedSetBuilder<Artifact> depsArchiveSourceMappingsBuilder = NestedSetBuilder.stableOrder(); |
| |
| for (J2ObjcMappingFileProvider mapping : getJ2ObjCMappings(ruleContext)) { |
| depsHeaderMappingsBuilder.addTransitive(mapping.getHeaderMappingFiles()); |
| depsClassMappingsBuilder.addTransitive(mapping.getClassMappingFiles()); |
| depsDependencyMappingsBuilder.addTransitive(mapping.getDependencyMappingFiles()); |
| depsArchiveSourceMappingsBuilder.addTransitive(mapping.getArchiveSourceMappingFiles()); |
| } |
| |
| return new J2ObjcMappingFileProvider( |
| depsHeaderMappingsBuilder.build(), |
| depsClassMappingsBuilder.build(), |
| depsDependencyMappingsBuilder.build(), |
| depsArchiveSourceMappingsBuilder.build()); |
| } |
| |
| private static ImmutableList<String> sourceJarFlags(RuleContext ruleContext) { |
| return ImmutableList.of( |
| "--output_gen_source_dir", |
| j2ObjcSourceJarTranslatedSourceTreeArtifact(ruleContext).getExecPathString(), |
| "--output_gen_header_dir", |
| j2objcSourceJarTranslatedHeaderTreeArtifact(ruleContext).getExecPathString()); |
| } |
| |
| private static J2ObjcMappingFileProvider createJ2ObjcTranspilationAction( |
| RuleContext ruleContext, |
| ImmutableList<Artifact> sources, |
| ImmutableList<Artifact> sourceJars, |
| J2ObjcMappingFileProvider depJ2ObjcMappingFileProvider, |
| JavaCompilationArgsProvider compArgsProvider, |
| J2ObjcSource j2ObjcSource) { |
| CustomCommandLine.Builder argBuilder = CustomCommandLine.builder(); |
| PathFragment javaExecutable = JavaCommon.getHostJavaExecutable(ruleContext); |
| argBuilder.add("--java", javaExecutable.getPathString()); |
| |
| Artifact j2ObjcDeployJar = ruleContext.getPrerequisiteArtifact("$j2objc", Mode.HOST); |
| argBuilder.addExecPath("--j2objc", j2ObjcDeployJar); |
| |
| argBuilder.add("--main_class").add("com.google.devtools.j2objc.J2ObjC"); |
| argBuilder.add("--objc_file_path").addPath(j2ObjcSource.getObjcFilePath()); |
| |
| Artifact outputDependencyMappingFile = j2ObjcOutputDependencyMappingFile(ruleContext); |
| argBuilder.addExecPath("--output_dependency_mapping_file", outputDependencyMappingFile); |
| |
| if (!sourceJars.isEmpty()) { |
| argBuilder.addExecPaths( |
| "--src_jars", VectorArg.join(",").each(ImmutableList.copyOf(sourceJars))); |
| argBuilder.addAll(sourceJarFlags(ruleContext)); |
| } |
| |
| List<String> translationFlags = |
| ruleContext.getFragment(J2ObjcConfiguration.class).getTranslationFlags(); |
| argBuilder.addAll(ImmutableList.copyOf(translationFlags)); |
| |
| NestedSet<Artifact> depsHeaderMappingFiles = |
| depJ2ObjcMappingFileProvider.getHeaderMappingFiles(); |
| if (!depsHeaderMappingFiles.isEmpty()) { |
| argBuilder.addExecPaths("--header-mapping", VectorArg.join(",").each(depsHeaderMappingFiles)); |
| } |
| |
| boolean experimentalJ2ObjcHeaderMap = |
| ruleContext.getFragment(J2ObjcConfiguration.class).experimentalJ2ObjcHeaderMap(); |
| Artifact outputHeaderMappingFile = j2ObjcOutputHeaderMappingFile(ruleContext); |
| if (!experimentalJ2ObjcHeaderMap) { |
| argBuilder.addExecPath("--output-header-mapping", outputHeaderMappingFile); |
| } |
| |
| NestedSet<Artifact> depsClassMappingFiles = depJ2ObjcMappingFileProvider.getClassMappingFiles(); |
| if (!depsClassMappingFiles.isEmpty()) { |
| argBuilder.addExecPaths("--mapping", VectorArg.join(",").each(depsClassMappingFiles)); |
| } |
| |
| Artifact archiveSourceMappingFile = j2ObjcOutputArchiveSourceMappingFile(ruleContext); |
| argBuilder.addExecPath("--output_archive_source_mapping_file", archiveSourceMappingFile); |
| |
| Artifact compiledLibrary = ObjcRuleClasses.j2objcIntermediateArtifacts(ruleContext).archive(); |
| argBuilder.addExecPath("--compiled_archive_file_path", compiledLibrary); |
| |
| Artifact bootclasspathJar = ruleContext.getPrerequisiteArtifact("$jre_emul_jar", Mode.HOST); |
| argBuilder.addFormatted("-Xbootclasspath:%s", bootclasspathJar); |
| |
| // A valid Java system module contains 3 files. The top directory contains a file "release". |
| ImmutableList<Artifact> moduleFiles = |
| ruleContext.getPrerequisiteArtifacts("$jre_emul_module", Mode.HOST).list(); |
| for (Artifact a : moduleFiles) { |
| if (a.getFilename().equals("release")) { |
| argBuilder.add("--system", a.getDirname()); |
| break; |
| } |
| } |
| |
| Artifact deadCodeReport = ruleContext.getPrerequisiteArtifact(":dead_code_report", Mode.HOST); |
| if (deadCodeReport != null) { |
| argBuilder.addExecPath("--dead-code-report", deadCodeReport); |
| } |
| |
| argBuilder.add("-d").addPath(j2ObjcSource.getObjcFilePath()); |
| |
| NestedSet<Artifact> compileTimeJars = compArgsProvider.getTransitiveCompileTimeJars(); |
| if (!compileTimeJars.isEmpty()) { |
| argBuilder.addExecPaths("-classpath", VectorArg.join(":").each(compileTimeJars)); |
| } |
| |
| argBuilder.addExecPaths(sources); |
| |
| SpawnAction.Builder transpilationAction = |
| new SpawnAction.Builder() |
| .setMnemonic("TranspilingJ2objc") |
| .setExecutable(ruleContext.getPrerequisiteArtifact("$j2objc_wrapper", Mode.HOST)) |
| .addInput(ruleContext.getPrerequisiteArtifact("$j2objc_wrapper", Mode.HOST)) |
| .addInput(j2ObjcDeployJar) |
| .addInput(bootclasspathJar) |
| .addInputs(moduleFiles) |
| .addInputs(sources) |
| .addInputs(sourceJars) |
| .addTransitiveInputs(compileTimeJars) |
| .addTransitiveInputs(JavaRuntimeInfo.forHost(ruleContext).javaBaseInputsMiddleman()) |
| .addTransitiveInputs(depsHeaderMappingFiles) |
| .addTransitiveInputs(depsClassMappingFiles) |
| .addCommandLine( |
| argBuilder.build(), |
| ParamFileInfo.builder(ParameterFile.ParameterFileType.UNQUOTED) |
| .setCharset(ISO_8859_1) |
| .setUseAlways(true) |
| .build()) |
| .addOutputs(j2ObjcSource.getObjcSrcs()) |
| .addOutputs(j2ObjcSource.getObjcHdrs()) |
| .addOutput(outputDependencyMappingFile) |
| .addOutput(archiveSourceMappingFile); |
| |
| if (deadCodeReport != null) { |
| transpilationAction.addInput(deadCodeReport); |
| } |
| |
| if (!experimentalJ2ObjcHeaderMap) { |
| transpilationAction.addOutput(outputHeaderMappingFile); |
| } |
| ruleContext.registerAction(transpilationAction.build(ruleContext)); |
| |
| if (experimentalJ2ObjcHeaderMap) { |
| CustomCommandLine.Builder headerMapCommandLine = CustomCommandLine.builder(); |
| if (!sources.isEmpty()) { |
| headerMapCommandLine.addExecPaths("--source_files", VectorArg.join(",").each(sources)); |
| } |
| if (!sourceJars.isEmpty()) { |
| headerMapCommandLine.addExecPaths("--source_jars", VectorArg.join(",").each(sourceJars)); |
| } |
| headerMapCommandLine.addExecPath("--output_mapping_file", outputHeaderMappingFile); |
| ruleContext.registerAction( |
| new SpawnAction.Builder() |
| .setMnemonic("GenerateJ2objcHeaderMap") |
| .setExecutable(ruleContext.getPrerequisiteArtifact("$j2objc_header_map", Mode.HOST)) |
| .addInput(ruleContext.getPrerequisiteArtifact("$j2objc_header_map", Mode.HOST)) |
| .addInputs(sources) |
| .addInputs(sourceJars) |
| .addCommandLine( |
| headerMapCommandLine.build(), |
| ParamFileInfo.builder(ParameterFileType.SHELL_QUOTED).build()) |
| .addOutput(outputHeaderMappingFile) |
| .build(ruleContext)); |
| } |
| |
| return new J2ObjcMappingFileProvider( |
| NestedSetBuilder.<Artifact>stableOrder().add(outputHeaderMappingFile).build(), |
| NestedSetBuilder.<Artifact>stableOrder().build(), |
| NestedSetBuilder.<Artifact>stableOrder().add(outputDependencyMappingFile).build(), |
| NestedSetBuilder.<Artifact>stableOrder().add(archiveSourceMappingFile).build()); |
| } |
| |
| private J2ObjcMappingFileProvider createJ2ObjcProtoCompileActions( |
| ConfiguredTarget base, |
| ProtoLangToolchainProvider protoToolchain, |
| RuleContext ruleContext, |
| ImmutableList<Artifact> filteredProtoSources, |
| J2ObjcSource j2ObjcSource) |
| throws InterruptedException { |
| ImmutableList<Artifact> outputHeaderMappingFiles = |
| ProtoCommon.getGeneratedOutputs(ruleContext, filteredProtoSources, ".j2objc.mapping"); |
| ImmutableList<Artifact> outputClassMappingFiles = |
| ProtoCommon.getGeneratedOutputs(ruleContext, filteredProtoSources, ".clsmap.properties"); |
| ImmutableList<Artifact> outputs = |
| ImmutableList.<Artifact>builder() |
| .addAll(j2ObjcSource.getObjcSrcs()) |
| .addAll(j2ObjcSource.getObjcHdrs()) |
| .addAll(outputHeaderMappingFiles) |
| .addAll(outputClassMappingFiles) |
| .build(); |
| |
| String genfilesPath = getProtoOutputRoot(ruleContext).getPathString(); |
| |
| ProtoInfo protoInfo = base.get(ProtoInfo.PROVIDER); |
| |
| ImmutableList.Builder<ProtoCompileActionBuilder.ToolchainInvocation> invocations = |
| ImmutableList.builder(); |
| invocations.add( |
| new ProtoCompileActionBuilder.ToolchainInvocation( |
| "j2objc", checkNotNull(protoToolchain), genfilesPath)); |
| ProtoCompileActionBuilder.registerActions( |
| ruleContext, |
| invocations.build(), |
| protoInfo, |
| ruleContext.getLabel(), |
| outputs, |
| "j2objc", |
| Exports.DO_NOT_USE, |
| shouldAllowProtoServices(ruleContext) ? Services.ALLOW : Services.DISALLOW); |
| |
| return new J2ObjcMappingFileProvider( |
| NestedSetBuilder.<Artifact>stableOrder().addAll(outputHeaderMappingFiles).build(), |
| NestedSetBuilder.<Artifact>stableOrder().addAll(outputClassMappingFiles).build(), |
| NestedSetBuilder.<Artifact>stableOrder().build(), |
| NestedSetBuilder.<Artifact>stableOrder().build()); |
| } |
| |
| private static List<? extends J2ObjcMappingFileProvider> getJ2ObjCMappings(RuleContext context) { |
| ImmutableList.Builder<J2ObjcMappingFileProvider> mappingFileProviderBuilder = |
| new ImmutableList.Builder<>(); |
| addJ2ObjCMappingsForAttribute(mappingFileProviderBuilder, context, "deps"); |
| addJ2ObjCMappingsForAttribute(mappingFileProviderBuilder, context, "runtime_deps"); |
| addJ2ObjCMappingsForAttribute(mappingFileProviderBuilder, context, "exports"); |
| return mappingFileProviderBuilder.build(); |
| } |
| |
| private static void addJ2ObjCMappingsForAttribute( |
| ImmutableList.Builder<J2ObjcMappingFileProvider> builder, RuleContext context, |
| String attributeName) { |
| if (context.attributes().has(attributeName, BuildType.LABEL_LIST)) { |
| for (TransitiveInfoCollection dependencyInfoDatum : |
| context.getPrerequisites(attributeName, Mode.TARGET)) { |
| J2ObjcMappingFileProvider provider = |
| dependencyInfoDatum.getProvider(J2ObjcMappingFileProvider.class); |
| if (provider != null) { |
| builder.add(provider); |
| } |
| } |
| } |
| } |
| |
| private static Artifact j2ObjcOutputHeaderMappingFile(RuleContext ruleContext) { |
| return ObjcRuleClasses.artifactByAppendingToBaseName(ruleContext, ".mapping.j2objc"); |
| } |
| |
| private static Artifact j2ObjcOutputDependencyMappingFile(RuleContext ruleContext) { |
| return ObjcRuleClasses.artifactByAppendingToBaseName(ruleContext, ".dependency_mapping.j2objc"); |
| } |
| |
| private static Artifact j2ObjcOutputArchiveSourceMappingFile(RuleContext ruleContext) { |
| return ObjcRuleClasses.artifactByAppendingToBaseName( |
| ruleContext, ".archive_source_mapping.j2objc"); |
| } |
| |
| private static Artifact j2ObjcSourceJarTranslatedSourceTreeArtifact(RuleContext ruleContext) { |
| PathFragment rootRelativePath = ruleContext |
| .getUniqueDirectory("_j2objc/src_jar_files") |
| .getRelative("source_files"); |
| return ruleContext.getTreeArtifact(rootRelativePath, ruleContext.getBinOrGenfilesDirectory()); |
| } |
| |
| /** |
| * Returns a unique path fragment for j2objc headers. The slightly shorter path is |
| * useful for very large app builds, which otherwise may have command lines that are |
| * too long to be executable. |
| */ |
| private static String j2objcHeaderBase(RuleContext ruleContext) { |
| boolean shorterPath = |
| ruleContext.getFragment(J2ObjcConfiguration.class).experimentalShorterHeaderPath(); |
| return shorterPath ? "_ios" : "_j2objc"; |
| } |
| |
| private static Artifact j2objcSourceJarTranslatedHeaderTreeArtifact(RuleContext ruleContext) { |
| String uniqueDirectoryPath = j2objcHeaderBase(ruleContext) + "/src_jar_files"; |
| PathFragment rootRelativePath = |
| ruleContext.getUniqueDirectory(uniqueDirectoryPath).getRelative("header_files"); |
| return ruleContext.getTreeArtifact(rootRelativePath, ruleContext.getBinOrGenfilesDirectory()); |
| } |
| |
| private static J2ObjcSource javaJ2ObjcSource( |
| RuleContext ruleContext, |
| ImmutableList<Artifact> javaInputSourceFiles, |
| ImmutableList<Artifact> javaSourceJarFiles) { |
| PathFragment objcFileRootRelativePath = |
| ruleContext.getUniqueDirectory(j2objcHeaderBase(ruleContext)); |
| PathFragment objcFileRootExecPath = ruleContext |
| .getConfiguration() |
| .getBinFragment() |
| .getRelative(objcFileRootRelativePath); |
| |
| // Note that these are mutable lists so that we can add the translated file info below. |
| List<Artifact> objcSrcs = |
| getOutputObjcFiles(ruleContext, javaInputSourceFiles, objcFileRootRelativePath, ".m"); |
| List<Artifact> objcHdrs = |
| getOutputObjcFiles(ruleContext, javaInputSourceFiles, objcFileRootRelativePath, ".h"); |
| List<PathFragment> headerSearchPaths = |
| j2objcSourceHeaderSearchPaths(ruleContext, objcFileRootExecPath, javaInputSourceFiles); |
| if (!javaSourceJarFiles.isEmpty()) { |
| // Add the translated source + header files. |
| objcSrcs.add(j2ObjcSourceJarTranslatedSourceTreeArtifact(ruleContext)); |
| Artifact translatedHeader = j2objcSourceJarTranslatedHeaderTreeArtifact(ruleContext); |
| objcHdrs.add(translatedHeader); |
| headerSearchPaths.add(translatedHeader.getExecPath()); |
| } |
| |
| return new J2ObjcSource( |
| ruleContext.getRule().getLabel(), |
| objcSrcs, |
| objcHdrs, |
| objcFileRootExecPath, |
| SourceType.JAVA, |
| headerSearchPaths); |
| } |
| |
| private static J2ObjcSource protoJ2ObjcSource( |
| RuleContext ruleContext, ImmutableList<Artifact> protoSources) throws InterruptedException { |
| PathFragment objcFileRootExecPath = getProtoOutputRoot(ruleContext); |
| |
| List<PathFragment> headerSearchPaths = |
| j2objcSourceHeaderSearchPaths(ruleContext, objcFileRootExecPath, protoSources); |
| |
| return new J2ObjcSource( |
| ruleContext.getTarget().getLabel(), |
| ProtoCommon.getGeneratedOutputs(ruleContext, protoSources, ".j2objc.pb.m"), |
| ProtoCommon.getGeneratedOutputs(ruleContext, protoSources, ".j2objc.pb.h"), |
| objcFileRootExecPath, |
| SourceType.PROTO, |
| headerSearchPaths); |
| } |
| |
| private static PathFragment getProtoOutputRoot(RuleContext ruleContext) |
| throws InterruptedException { |
| return ruleContext |
| .getConfiguration() |
| .getGenfilesFragment() |
| .getRelative( |
| ruleContext |
| .getLabel() |
| .getPackageIdentifier() |
| .getRepository() |
| .getExecPath( |
| ruleContext |
| .getAnalysisEnvironment() |
| .getSkylarkSemantics() |
| .experimentalSiblingRepositoryLayout())); |
| } |
| |
| private static boolean isProtoRule(ConfiguredTarget base) { |
| return base.get(ProtoInfo.PROVIDER) != null; |
| } |
| |
| /** Returns a mutable List of objc output files. */ |
| private static List<Artifact> getOutputObjcFiles( |
| RuleContext ruleContext, |
| Collection<Artifact> javaSrcs, |
| PathFragment objcFileRootRelativePath, |
| String suffix) { |
| List<Artifact> objcSources = new ArrayList<>(); |
| for (Artifact javaSrc : javaSrcs) { |
| objcSources.add(ruleContext.getRelatedArtifact( |
| objcFileRootRelativePath.getRelative(javaSrc.getExecPath()), suffix)); |
| } |
| return objcSources; |
| } |
| |
| /** |
| * Returns a mutable list of header search paths necessary to compile the J2ObjC-generated code |
| * from a single target. |
| * |
| * @param ruleContext the rule context |
| * @param objcFileRootExecPath the exec path under which all J2ObjC-generated file resides |
| * @param sourcesToTranslate the source files to be translated by J2ObjC in a single target |
| */ |
| private static List<PathFragment> j2objcSourceHeaderSearchPaths( |
| RuleContext ruleContext, |
| PathFragment objcFileRootExecPath, |
| Collection<Artifact> sourcesToTranslate) { |
| PathFragment genRoot = ruleContext.getConfiguration().getGenfilesFragment(); |
| List<PathFragment> headerSearchPaths = new ArrayList<>(); |
| headerSearchPaths.add(objcFileRootExecPath); |
| // We add another header search path with gen root if we have generated sources to translate. |
| for (Artifact sourceToTranslate : sourcesToTranslate) { |
| if (!sourceToTranslate.isSourceArtifact()) { |
| headerSearchPaths.add(objcFileRootExecPath.getRelative(genRoot)); |
| return headerSearchPaths; |
| } |
| } |
| |
| return headerSearchPaths; |
| } |
| |
| /** Sets up and returns an {@link ObjcCommon} object containing the J2ObjC-translated code. */ |
| private static ObjcCommon common( |
| RuleContext ruleContext, |
| List<Artifact> transpiledSources, |
| List<Artifact> transpiledHeaders, |
| List<PathFragment> headerSearchPaths, |
| List<Attribute> dependentAttributes, |
| List<TransitiveInfoCollection> otherObjcProviders) |
| throws InterruptedException { |
| ObjcCommon.Builder builder = new ObjcCommon.Builder(ruleContext); |
| IntermediateArtifacts intermediateArtifacts = |
| ObjcRuleClasses.j2objcIntermediateArtifacts(ruleContext); |
| |
| if (!transpiledSources.isEmpty() || !transpiledHeaders.isEmpty()) { |
| CompilationArtifacts compilationArtifacts = new CompilationArtifacts.Builder() |
| .addNonArcSrcs(transpiledSources) |
| .setIntermediateArtifacts(intermediateArtifacts) |
| .addAdditionalHdrs(transpiledHeaders) |
| .build(); |
| builder.setCompilationArtifacts(compilationArtifacts); |
| builder.setHasModuleMap(); |
| } |
| |
| for (Attribute dependentAttribute : dependentAttributes) { |
| if (ruleContext.attributes().has(dependentAttribute.getName(), BuildType.LABEL_LIST) |
| || ruleContext.attributes().has(dependentAttribute.getName(), BuildType.LABEL)) { |
| builder.addDepObjcProviders( |
| ruleContext.getPrerequisites( |
| dependentAttribute.getName(), |
| dependentAttribute.getAccessMode(), |
| ObjcProvider.SKYLARK_CONSTRUCTOR)); |
| } |
| } |
| |
| List<ObjcProvider> newOtherDeps = |
| otherObjcProviders |
| .stream() |
| .map(d -> d.get(ObjcProvider.SKYLARK_CONSTRUCTOR)) |
| .collect(Collectors.toList()); |
| // We can't just use addDeps since that now takes ConfiguredTargetAndTargets and we only have |
| // TransitiveInfoCollections |
| builder.addDepObjcProviders(newOtherDeps); |
| |
| return builder |
| .addIncludes(headerSearchPaths) |
| .setIntermediateArtifacts(intermediateArtifacts) |
| .build(); |
| } |
| } |