// 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.objc; import static com.google.common.truth.Truth.assertThat; import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.getFirstArtifactEndingWith; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.AbstractAction; import com.google.devtools.build.lib.actions.Action; import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; import com.google.devtools.build.lib.actions.ActionExecutionContext; import com.google.devtools.build.lib.actions.ActionInputHelper; import com.google.devtools.build.lib.actions.ActionInputPrefetcher; import com.google.devtools.build.lib.actions.ActionTemplate.ActionTemplateExpansionException; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact; import com.google.devtools.build.lib.actions.Artifact.TreeFileArtifact; import com.google.devtools.build.lib.actions.ArtifactOwner; import com.google.devtools.build.lib.actions.CommandAction; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.packages.NativeAspectClass; import com.google.devtools.build.lib.rules.apple.AppleConfiguration; import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType; import com.google.devtools.build.lib.rules.apple.AppleToolchain; import com.google.devtools.build.lib.rules.apple.DottedVersion; import com.google.devtools.build.lib.rules.cpp.CppCompileActionTemplate; import com.google.devtools.build.lib.rules.cpp.CppModuleMapAction; import com.google.devtools.build.lib.rules.cpp.UmbrellaHeaderAction; import com.google.devtools.build.lib.syntax.SkylarkSemantics; import com.google.devtools.build.lib.testutil.TestConstants; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; import java.io.ByteArrayOutputStream; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Unit test for Java source file translation into ObjC in {@link * com.google.devtools.build.lib.rules.java.JavaLibrary} and translated file compilation and linking * in {@link ObjcBinary} and {@link J2ObjcLibrary}. */ @RunWith(JUnit4.class) public class BazelJ2ObjcLibraryTest extends J2ObjcLibraryTest { protected NativeAspectClass getJ2ObjcAspect() { return ruleClassProvider.getNativeAspectClass(J2ObjcAspect.NAME); } /** * Gets the target with the given label, using the apple_binary multi-arch split transition with * the default version of iOS as the platform. */ private ConfiguredTarget getConfiguredTargetInAppleBinaryTransition(String label) throws Exception { BuildConfiguration childConfig = Iterables.getOnlyElement( getSplitConfigurations( targetConfig, new MultiArchSplitTransitionProvider.AppleBinaryTransition( PlatformType.IOS, Optional.absent()))); return getConfiguredTarget(label, childConfig); } @Test public void testJ2ObjCInformationExportedFromJ2ObjcLibrary() throws Exception { ConfiguredTarget j2objcLibraryTarget = getConfiguredTarget( "//java/com/google/dummy/test:transpile"); ObjcProvider provider = j2objcLibraryTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a", "java/com/google/dummy/test/libtest_j2objc.a"); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h"); String execPath = getConfiguration(j2objcLibraryTarget).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/"; assertThat( Iterables.transform( provider.get(ObjcProvider.INCLUDE), PathFragment::getSafePathString)) .containsExactly(execPath + "java/com/google/dummy/test/_j2objc/test"); } @Test public void testJ2ObjCInformationExportedWithGeneratedJavaSources() throws Exception { scratch.file("java/com/google/test/in.txt"); scratch.file( "java/com/google/test/BUILD", "package(default_visibility=['//visibility:public'])", "genrule(", " name = 'dummy_gen',", " srcs = ['in.txt'],", " outs = ['test.java'],", " cmd = 'dummy'", ")", "", "java_library(", " name = 'test',", " srcs = [':test.java']", ")", "j2objc_library(", " name = 'transpile',", " deps = ['test'],", ")"); ConfiguredTarget target = getConfiguredTarget("//java/com/google/test:transpile"); ObjcProvider provider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR); String genfilesFragment = getConfiguration(target).getGenfilesFragment().toString(); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a", "java/com/google/test/libtest_j2objc.a"); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/test/_j2objc/test/" + genfilesFragment + "/java/com/google/test/test.h"); String execPath = getConfiguration(target).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/"; assertThat( Iterables.transform( provider.get(ObjcProvider.INCLUDE), PathFragment::getSafePathString)) .containsExactly( execPath + "java/com/google/test/_j2objc/test/" + genfilesFragment, execPath + "java/com/google/test/_j2objc/test"); } @Test public void testJ2ObjcProtoRuntimeLibraryAndHeadersExported() throws Exception { scratch.file("java/com/google/dummy/test/proto/test.java"); scratch.file("java/com/google/dummy/test/proto/test.proto"); scratch.file( "java/com/google/dummy/test/proto/BUILD", "package(default_visibility=['//visibility:public'])", "proto_library(", " name = 'test_proto',", " srcs = ['test.proto'],", ")", "java_proto_library(", " name = 'test_java_proto',", " deps = [':test_proto'],", ")", "java_library(", " name = 'test',", " srcs = ['test.java'],", " deps = [':test_java_proto']", ")", "j2objc_library(", " name = 'transpile',", " deps = ['test']", ")"); ConfiguredTarget j2objcLibraryTarget = getConfiguredTarget( "//java/com/google/dummy/test/proto:transpile"); ObjcProvider provider = j2objcLibraryTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a", TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libproto_runtime.a", "java/com/google/dummy/test/proto/libtest_j2objc.a", "java/com/google/dummy/test/proto/libtest_proto_j2objc.a"); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/runtime.h", "java/com/google/dummy/test/proto/test.j2objc.pb.h", "java/com/google/dummy/test/proto/_j2objc/test/java/com/google/dummy/test/proto/test.h"); } @Test public void testJ2ObjcHeaderMapExportedInJavaLibrary() throws Exception { scratch.file( "java/com/google/transpile/BUILD", "java_library(", " name = 'dummy',", " srcs = ['dummy.java']", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget("//java/com/google/transpile:dummy"); J2ObjcMappingFileProvider provider = target.getProvider(J2ObjcMappingFileProvider.class); assertThat(Iterables.getOnlyElement(provider.getHeaderMappingFiles()) .getRootRelativePath().toString()).isEqualTo( "java/com/google/transpile/dummy.mapping.j2objc"); } @Test public void testDepsJ2ObjcHeaderMapExportedInJavaLibraryWithNoSourceFile() throws Exception { scratch.file( "java/com/google/transpile/BUILD", "java_library(", " name = 'dummy',", " exports = ['//java/com/google/dep:dep'],", ")"); scratch.file( "java/com/google/dep/BUILD", "java_library(", " name = 'dep',", " srcs = ['dummy.java'],", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget("//java/com/google/transpile:dummy"); J2ObjcMappingFileProvider provider = target.getProvider(J2ObjcMappingFileProvider.class); assertThat(Iterables.getOnlyElement(provider.getHeaderMappingFiles()) .getRootRelativePath().toString()).isEqualTo( "java/com/google/dep/dep.mapping.j2objc"); } @Test public void testJ2ObjcProtoClassMappingFilesExportedInJavaLibrary() throws Exception { scratch.file("java/com/google/dummy/test/proto/test.java"); scratch.file("java/com/google/dummy/test/proto/test.proto"); scratch.file( "java/com/google/dummy/test/proto/BUILD", "package(default_visibility=['//visibility:public'])", "proto_library(", " name = 'test_proto',", " srcs = ['test.proto'],", ")", "java_proto_library(", " name = 'test_java_proto',", " deps = [':test_proto'],", ")", "java_library(", " name = 'test',", " srcs = ['test.java'],", " deps = [':test_java_proto']", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget( "//java/com/google/dummy/test/proto:test"); J2ObjcMappingFileProvider provider = target.getProvider(J2ObjcMappingFileProvider.class); Artifact classMappingFile = getGenfilesArtifact( "test.clsmap.properties", getConfiguredTarget( "//java/com/google/dummy/test/proto:test_proto", getAppleCrosstoolConfiguration()), getJ2ObjcAspect()); assertThat(provider.getClassMappingFiles()).containsExactly(classMappingFile); } @Test public void testJavaProtoLibraryWithProtoLibrary() throws Exception { scratch.file( "x/BUILD", "proto_library(", " name = 'test_proto',", " srcs = ['test.proto'],", ")", "java_proto_library(", " name = 'test_java_proto',", " deps = [':test_proto'],", ")", "java_library(", " name = 'test',", " srcs = ['test.java'],", " deps = [':test_java_proto']", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget("//x:test"); ConfiguredTarget test = getConfiguredTarget("//x:test_proto", getAppleCrosstoolConfiguration()); J2ObjcMappingFileProvider provider = target.getProvider(J2ObjcMappingFileProvider.class); Artifact classMappingFile = getGenfilesArtifact("test.clsmap.properties", test, getJ2ObjcAspect()); assertThat(provider.getClassMappingFiles()).containsExactly(classMappingFile); ObjcProvider objcProvider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR); Artifact headerFile = getGenfilesArtifact("test.j2objc.pb.h", test, getJ2ObjcAspect()); Artifact sourceFile = getGenfilesArtifact("test.j2objc.pb.m", test, getJ2ObjcAspect()); assertThat(objcProvider.get(ObjcProvider.HEADER)).contains(headerFile); assertThat(objcProvider.get(ObjcProvider.SOURCE)).contains(sourceFile); } @Test public void testJavaProtoLibraryWithProtoLibrary_external() throws Exception { scratch.file("/bla/WORKSPACE"); // Create the rule '@bla//foo:test_proto'. scratch.file( "/bla/foo/BUILD", "package(default_visibility=['//visibility:public'])", "proto_library(", " name = 'test_proto',", " srcs = ['test.proto'],", ")", "java_proto_library(", " name = 'test_java_proto',", " deps = [':test_proto'])", ""); String existingWorkspace = new String(FileSystemUtils.readContentAsLatin1(rootDirectory.getRelative("WORKSPACE"))); scratch.overwriteFile( "WORKSPACE", "local_repository(name = 'bla', path = '/bla/')", existingWorkspace); invalidatePackages(); // A dash of magic to re-evaluate the WORKSPACE file. scratch.file( "x/BUILD", "", "java_library(", " name = 'test',", " srcs = ['test.java'],", " deps = ['@bla//foo:test_java_proto'])"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget("//x:test"); ConfiguredTarget test = getConfiguredTarget("@bla//foo:test_proto", getAppleCrosstoolConfiguration()); J2ObjcMappingFileProvider provider = target.getProvider(J2ObjcMappingFileProvider.class); Artifact classMappingFile = getGenfilesArtifact("../external/bla/foo/test.clsmap.properties", test, getJ2ObjcAspect()); assertThat(provider.getClassMappingFiles()).containsExactly(classMappingFile); ObjcProvider objcProvider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR); Artifact headerFile = getGenfilesArtifact("../external/bla/foo/test.j2objc.pb.h", test, getJ2ObjcAspect()); Artifact sourceFile = getGenfilesArtifact("../external/bla/foo/test.j2objc.pb.m", test, getJ2ObjcAspect()); assertThat(objcProvider.get(ObjcProvider.HEADER)).contains(headerFile); assertThat(objcProvider.get(ObjcProvider.SOURCE)).contains(sourceFile); assertThat(objcProvider.get(ObjcProvider.INCLUDE)) .contains(getConfiguration(target).getGenfilesFragment().getRelative("external/bla")); } @Test public void testJ2ObjcInfoExportedInJavaImport() throws Exception { scratch.file( "java/com/google/transpile/BUILD", "java_import(", " name = 'dummy',", " jars = ['dummy.jar'],", " srcjar = 'dummy.srcjar',", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget("//java/com/google/transpile:dummy"); J2ObjcMappingFileProvider provider = target.getProvider(J2ObjcMappingFileProvider.class); assertThat(Iterables.getOnlyElement(provider.getHeaderMappingFiles()) .getRootRelativePath().toString()).isEqualTo( "java/com/google/transpile/dummy.mapping.j2objc"); assertThat(provider.getClassMappingFiles()).isEmpty(); } protected void checkObjcArchiveAndLinkActions( String targetLabel, String archiveFileName, String objFileName, Iterable compilationInputExecPaths) throws Exception { String labelName = Label.parseAbsolute(targetLabel, ImmutableMap.of()).getName(); CommandAction linkAction = (CommandAction) getGeneratingAction( getBinArtifact( String.format("%s_bin", labelName), getConfiguredTargetInAppleBinaryTransition(targetLabel))); checkObjcCompileActions( getFirstArtifactEndingWith(linkAction.getInputs(), archiveFileName), objFileName, compilationInputExecPaths); } @Test public void testMissingEntryClassesError() throws Exception { useConfiguration("--j2objc_dead_code_removal"); checkError("java/com/google/dummy", "transpile", J2ObjcLibrary.NO_ENTRY_CLASS_ERROR_MSG, "j2objc_library(name = 'transpile', deps = ['//java/com/google/dummy/test:test'])"); } @Test public void testNoJ2ObjcDeadCodeRemovalActionWithoutOptFlag() throws Exception { useConfiguration("--noj2objc_dead_code_removal"); addSimpleJ2ObjcLibraryWithEntryClasses(); addSimpleBinaryTarget("//java/com/google/app/test:transpile"); Artifact expectedPrunedSource = getBinArtifact( "_j2objc_pruned/app/java/com/google/app/test/_j2objc/test/" + "java/com/google/app/test/test_pruned.m", getConfiguredTarget("//app:app")); assertThat(getGeneratingAction(expectedPrunedSource)).isNull(); } @Test public void testExplicitJreDeps() throws Exception { ConfiguredTarget j2objcLibraryTarget = getConfiguredTarget( "//java/com/google/dummy/test:transpile"); ObjcProvider provider = j2objcLibraryTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); // jre_io_lib and jre_emul_lib should be excluded. assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a", "java/com/google/dummy/test/libtest_j2objc.a"); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h"); } @Test public void testTranspilationActionTreeArtifactOutputsFromSourceJar() throws Exception { useConfiguration("--ios_cpu=i386", "--ios_minimum_os=1.0"); scratch.file("java/com/google/transpile/dummy.java"); scratch.file("java/com/google/transpile/dummyjar.srcjar"); scratch.file( "java/com/google/transpile/BUILD", "java_library(", " name = 'dummy',", " srcs = ['dummy.java', 'dummyjar.srcjar'],", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget("//java/com/google/transpile:dummy"); ObjcProvider provider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR); Artifact srcJarSources = getFirstArtifactEndingWith( provider.get(ObjcProvider.SOURCE), "source_files"); Artifact srcJarHeaders = getFirstArtifactEndingWith( provider.get(ObjcProvider.HEADER), "header_files"); assertThat(srcJarSources.getRootRelativePathString()) .isEqualTo("java/com/google/transpile/_j2objc/src_jar_files/dummy/source_files"); assertThat(srcJarHeaders.getRootRelativePathString()) .isEqualTo("java/com/google/transpile/_j2objc/src_jar_files/dummy/header_files"); assertThat(srcJarSources.isTreeArtifact()).isTrue(); assertThat(srcJarHeaders.isTreeArtifact()).isTrue(); } @Test public void testGeneratedTreeArtifactFromGenJar() throws Exception { useConfiguration("--ios_cpu=i386", "--ios_minimum_os=1.0"); addSimpleJ2ObjcLibraryWithJavaPlugin(); ConfiguredTarget j2objcLibraryTarget = getConfiguredTarget("//java/com/google/app/test:transpile"); ObjcProvider provider = j2objcLibraryTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); Artifact headers = getFirstArtifactEndingWith(provider.get(ObjcProvider.HEADER), "header_files"); Artifact sources = getFirstArtifactEndingWith(provider.get(ObjcProvider.SOURCE), "source_files"); assertThat(headers.isTreeArtifact()).isTrue(); assertThat(sources.isTreeArtifact()).isTrue(); SpawnAction j2objcAction = (SpawnAction) getGeneratingAction(headers); assertThat(j2objcAction.getOutputs()).containsAllOf(headers, sources); assertContainsSublist( ImmutableList.copyOf(paramFileArgsForAction(j2objcAction)), ImmutableList.of( "--output_gen_source_dir", sources.getExecPathString(), "--output_gen_header_dir", headers.getExecPathString())); } @Test public void testJ2ObjcHeaderMappingAction() throws Exception { scratch.file( "java/com/google/transpile/BUILD", "java_library(", " name = 'lib1',", " srcs = ['libOne.java', 'jar.srcjar'],", " deps = [':lib2']", ")", "", "java_library(", " name = 'lib2',", " srcs = ['libTwo.java'],", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget( "//java/com/google/transpile:lib1"); J2ObjcMappingFileProvider mappingFileProvider = target.getProvider(J2ObjcMappingFileProvider.class); assertThat(Artifact.toRootRelativePaths(mappingFileProvider.getHeaderMappingFiles())) .containsExactly( "java/com/google/transpile/lib1.mapping.j2objc", "java/com/google/transpile/lib2.mapping.j2objc"); Artifact mappingFile = getFirstArtifactEndingWith( mappingFileProvider.getHeaderMappingFiles(), "lib1.mapping.j2objc"); SpawnAction headerMappingAction = (SpawnAction) getGeneratingAction(mappingFile); String execPath = getConfiguration(target).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/"; assertThat(Artifact.toRootRelativePaths(headerMappingAction.getInputs())) .containsAllOf( "java/com/google/transpile/libOne.java", "java/com/google/transpile/jar.srcjar"); assertThat(headerMappingAction.getArguments()) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "tools/j2objc/j2objc_header_map.py", "--source_files", "java/com/google/transpile/libOne.java", "--source_jars", "java/com/google/transpile/jar.srcjar", "--output_mapping_file", execPath + "java/com/google/transpile/lib1.mapping.j2objc") .inOrder(); } protected void checkObjcCompileActions( Artifact archiveFile, String objFileName, Iterable compilationInputExecPaths) throws Exception { CommandAction compileAction = getObjcCompileAction(archiveFile, objFileName); assertThat(Artifact.toRootRelativePaths(compileAction.getPossibleInputsForTesting())) .containsAllIn(compilationInputExecPaths); } protected CommandAction getObjcCompileAction(Artifact archiveFile, String objFileName) throws Exception { CommandAction archiveAction = (CommandAction) getGeneratingAction(archiveFile); CommandAction compileAction = (CommandAction) getGeneratingAction(getFirstArtifactEndingWith(archiveAction.getInputs(), objFileName)); return compileAction; } protected void addSimpleBinaryTarget(String j2objcLibraryTargetDep) throws Exception { scratch.file("app/app.m"); scratch.file("app/Info.plist"); scratch.file( "app/BUILD", "package(default_visibility=['//visibility:public'])", "objc_library(", " name = 'lib',", " deps = ['" + j2objcLibraryTargetDep + "'])", "", "apple_binary(", " name = 'app',", " platform_type = 'ios',", " deps = [':main_lib'],", ")", "objc_library(", " name = 'main_lib',", " srcs = ['app.m'],", " deps = [':lib'],", ")"); } protected void addSimpleJ2ObjcLibraryWithEntryClasses() throws Exception { scratch.file("java/com/google/app/test/test.java"); scratch.file( "java/com/google/app/test/BUILD", "package(default_visibility=['//visibility:public'])", "java_library(", " name = 'test',", " srcs = ['test.java'],", ")", "j2objc_library(", " name = 'transpile',", " entry_classes = ['com.google.app.test.test'],", " deps = ['test'],", ")"); } protected void addSimpleJ2ObjcLibraryWithJavaPlugin() throws Exception { scratch.file("java/com/google/app/test/test.java"); scratch.file("java/com/google/app/test/plugin.java"); scratch.file( "java/com/google/app/test/BUILD", "package(default_visibility=['//visibility:public'])", "java_library(", " name = 'test',", " srcs = ['test.java'],", " plugins = [':plugin'],", ")", "java_plugin(", " name = 'plugin',", " processor_class = 'com.google.process.stuff',", " srcs = ['plugin.java'],", ")", "j2objc_library(", " name = 'transpile',", " deps = [':test']", ")"); } protected Artifact j2objcArchive(String j2objcLibraryTarget, String javaTargetName) throws Exception { ConfiguredTarget target = getConfiguredTarget(j2objcLibraryTarget); ObjcProvider provider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR); String archiveName = String.format("lib%s_j2objc.a", javaTargetName); return getFirstArtifactEndingWith(provider.get(ObjcProvider.LIBRARY), archiveName); } @Test public void testJ2ObjcInformationExportedFromObjcLibrary() throws Exception { scratch.file("app/lib.m"); scratch.file( "app/BUILD", "package(default_visibility=['//visibility:public'])", "objc_library(", " name = 'lib',", " srcs = ['lib.m'],", " deps = ['//java/com/google/dummy/test:transpile'],", ")"); ConfiguredTarget objcTarget = getConfiguredTarget("//app:lib"); ObjcProvider provider = objcTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a", "java/com/google/dummy/test/libtest_j2objc.a", "app/liblib.a"); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h"); String execPath = getConfiguration(objcTarget).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/"; assertThat( Iterables.transform( provider.get(ObjcProvider.INCLUDE), PathFragment::getSafePathString)) .containsExactly(execPath + "java/com/google/dummy/test/_j2objc/test"); } @Test public void testJ2ObjcInfoExportedInObjcLibraryFromRuntimeDeps() throws Exception { scratch.file("app/lib.m"); scratch.file( "app/BUILD", "package(default_visibility=['//visibility:public'])", "", "java_library(", " name = 'dummyOne',", " srcs = ['dummyOne.java'],", ")", "java_library(", " name = 'dummyTwo',", " srcs = ['dummyTwo.java'],", " runtime_deps = [':dummyOne'],", ")", "j2objc_library(", " name = 'transpile',", " deps = [':dummyTwo'],", ")", "objc_library(", " name = 'lib',", " srcs = ['lib.m'],", " deps = ['//app:transpile'],", ")"); ConfiguredTarget objcTarget = getConfiguredTarget("//app:lib"); ObjcProvider provider = objcTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a", "app/libdummyOne_j2objc.a", "app/libdummyTwo_j2objc.a", "app/liblib.a"); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "app/_j2objc/dummyOne/app/dummyOne.h", "app/_j2objc/dummyTwo/app/dummyTwo.h"); String execPath = getConfiguration(objcTarget).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/"; assertThat( Iterables.transform( provider.get(ObjcProvider.INCLUDE), PathFragment::getSafePathString)) .containsExactly(execPath + "app/_j2objc/dummyOne", execPath + "app/_j2objc/dummyTwo"); } @Test public void testJ2ObjcAppearsInLinkArgs() throws Exception { scratch.file( "java/c/y/BUILD", "java_library(", " name = 'ylib',", " srcs = ['lib.java'],", ")"); scratch.file( "x/BUILD", "j2objc_library(", " name = 'j2',", " deps = [ '//java/c/y:ylib' ],", " jre_deps = [ '" + TestConstants.TOOLS_REPOSITORY + "//third_party/java/j2objc:jre_io_lib' ],", ")", "apple_binary(", " name = 'test',", " platform_type = 'ios',", " deps = [':main_lib'],", ")", "objc_library(", " name = 'main_lib',", " srcs = ['test.m'],", " deps = [':j2'],", ")"); CommandAction linkAction = linkAction("//x:test"); ConfiguredTarget target = getConfiguredTargetInAppleBinaryTransition("//x:test"); String binDir = getConfiguration(target).getBinDirectory(RepositoryName.MAIN).getExecPathString(); assertThat(paramFileArgsForAction(linkAction)) .containsAllOf( binDir + "/java/c/y/libylib_j2objc.a", // All jre libraries mus appear after java libraries in the link order. binDir + "/" + TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_io_lib.a", binDir + "/" + TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a") .inOrder(); } @Test public void testArchiveLinkActionWithTreeArtifactFromGenJar() throws Exception { useConfiguration("--ios_cpu=i386", "--ios_minimum_os=1.0"); addSimpleJ2ObjcLibraryWithJavaPlugin(); Artifact archive = j2objcArchive("//java/com/google/app/test:transpile", "test"); CommandAction archiveAction = (CommandAction) getGeneratingAction(archive); Artifact objectFilesFromGenJar = getFirstArtifactEndingWith(archiveAction.getInputs(), "source_files"); Artifact normalObjectFile = getFirstArtifactEndingWith(archiveAction.getInputs(), "test.o"); // Test that the archive obj list param file contains the individual object files inside // the object file tree artifact. assertThat(paramFileCommandLineForAction(archiveAction).arguments(DUMMY_ARTIFACT_EXPANDER)) .containsExactly( objectFilesFromGenJar.getExecPathString() + "/children1", objectFilesFromGenJar.getExecPathString() + "/children2", normalObjectFile.getExecPathString()); } @Test public void testJ2ObjCCustomModuleMap() throws Exception { useConfiguration("--experimental_objc_enable_module_maps"); scratch.file("java/com/google/transpile/dummy.java"); scratch.file( "java/com/google/transpile/BUILD", "package(default_visibility=['//visibility:public'])", "java_library(", " name = 'dummy',", " srcs = ['dummy.java'],", ")"); ConfiguredTarget target = getJ2ObjCAspectConfiguredTarget("//java/com/google/transpile:dummy"); ObjcProvider provider = target.get(ObjcProvider.SKYLARK_CONSTRUCTOR); Artifact moduleMap = getFirstArtifactEndingWith( provider.get(ObjcProvider.MODULE_MAP), "dummy.modulemaps/module.modulemap"); Artifact umbrellaHeader = getFirstArtifactEndingWith( provider.get(ObjcProvider.UMBRELLA_HEADER), "dummy.modulemaps/umbrella.h"); CppModuleMapAction moduleMapAction = (CppModuleMapAction) getGeneratingAction(moduleMap); UmbrellaHeaderAction umbrellaHeaderAction = (UmbrellaHeaderAction) getGeneratingAction(umbrellaHeader); ActionExecutionContext dummyActionExecutionContext = new ActionExecutionContext( null, null, ActionInputPrefetcher.NONE, actionKeyContext, null, null, ImmutableMap.of(), ImmutableMap.of(), DUMMY_ARTIFACT_EXPANDER, null); ByteArrayOutputStream moduleMapStream = new ByteArrayOutputStream(); ByteArrayOutputStream umbrellaHeaderStream = new ByteArrayOutputStream(); moduleMapAction.newDeterministicWriter(dummyActionExecutionContext) .writeOutputFile(moduleMapStream); umbrellaHeaderAction.newDeterministicWriter(dummyActionExecutionContext) .writeOutputFile(umbrellaHeaderStream); String moduleMapContent = moduleMapStream.toString(); String umbrellaHeaderContent = umbrellaHeaderStream.toString(); assertThat(moduleMapContent).contains("umbrella header \"umbrella.h\""); assertThat(umbrellaHeaderContent).contains("/dummy.h"); assertThat(umbrellaHeaderContent).contains("#include"); } @Test public void testModuleMapFromGenJarTreeArtifact() throws Exception { useConfiguration("--ios_cpu=i386", "--ios_minimum_os=1.0"); addSimpleJ2ObjcLibraryWithJavaPlugin(); ConfiguredTarget j2objcLibraryTarget = getConfiguredTarget("//java/com/google/app/test:transpile"); ObjcProvider provider = j2objcLibraryTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); Artifact moduleMap = getFirstArtifactEndingWith( provider.get(ObjcProvider.MODULE_MAP), "test.modulemaps/module.modulemap"); Artifact umbrellaHeader = getFirstArtifactEndingWith( provider.get(ObjcProvider.UMBRELLA_HEADER), "test.modulemaps/umbrella.h"); CppModuleMapAction moduleMapAction = (CppModuleMapAction) getGeneratingAction(moduleMap); UmbrellaHeaderAction umbrellaHeaderAction = (UmbrellaHeaderAction) getGeneratingAction(umbrellaHeader); Artifact headers = getFirstArtifactEndingWith(provider.get(ObjcProvider.HEADER), "header_files"); // Test that the module map action contains the header tree artifact as both the public header // and part of the action inputs. assertThat(moduleMapAction.getPublicHeaders()).contains(headers); assertThat(moduleMapAction.getInputs()).contains(headers); ActionExecutionContext dummyActionExecutionContext = new ActionExecutionContext( null, null, ActionInputPrefetcher.NONE, actionKeyContext, null, null, ImmutableMap.of(), ImmutableMap.of(), DUMMY_ARTIFACT_EXPANDER, null); ByteArrayOutputStream moduleMapStream = new ByteArrayOutputStream(); ByteArrayOutputStream umbrellaHeaderStream = new ByteArrayOutputStream(); moduleMapAction .newDeterministicWriter(dummyActionExecutionContext) .writeOutputFile(moduleMapStream); umbrellaHeaderAction .newDeterministicWriter(dummyActionExecutionContext) .writeOutputFile(umbrellaHeaderStream); String moduleMapContent = moduleMapStream.toString(); String umbrellaHeaderContent = umbrellaHeaderStream.toString(); // Test that the module map content contains the individual headers inside the header tree // artifact. assertThat(moduleMapContent).contains("umbrella header \"umbrella.h\""); assertThat(umbrellaHeaderContent).contains(headers.getExecPathString() + "/children1"); assertThat(umbrellaHeaderContent).contains(headers.getExecPathString() + "/children2"); } @Test public void testJ2ObjCFullyLinkAction() throws Exception { AbstractAction linkAction = (AbstractAction) getGeneratingActionForLabel( "//java/com/google/dummy/test:transpile_fully_linked.a"); String fullyLinkBinaryPath = Iterables.getOnlyElement(linkAction.getOutputs()).getExecPathString(); assertThat(fullyLinkBinaryPath).contains("transpile_fully_linked.a"); } @Test public void testObjcCompileAction() throws Exception { Artifact archive = j2objcArchive("//java/com/google/dummy/test:transpile", "test"); CommandAction compileAction = getObjcCompileAction(archive, "test.o"); assertThat(Artifact.toRootRelativePaths(compileAction.getPossibleInputsForTesting())) .containsAllOf( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.m"); assertThat(compileAction.getArguments()).containsAllOf("-fno-objc-arc", "-fno-strict-overflow"); } @Test public void testJ2ObjcSourcesCompilationAndLinking() throws Exception { addSimpleBinaryTarget("//java/com/google/dummy/test:transpile"); checkObjcArchiveAndLinkActions( "//app:app", "libtest_j2objc.a", "test.o", ImmutableList.of( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.m")); } @Test public void testNestedJ2ObjcLibraryDeps() throws Exception { scratch.file("java/com/google/dummy/dummy.java"); scratch.file( "java/com/google/dummy/BUILD", "package(default_visibility=['//visibility:public'])", "java_library(", " name = 'dummy',", " srcs = ['dummy.java'],", ")", "", "j2objc_library(", " name = 'transpile',", " deps = [", " ':dummy',", " '//java/com/google/dummy/test:transpile',", " ])"); addSimpleBinaryTarget("//java/com/google/dummy:transpile"); checkObjcArchiveAndLinkActions( "//app:app", "libtest_j2objc.a", "test.o", ImmutableList.of( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h", "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.m")); checkObjcArchiveAndLinkActions( "//app:app", "libdummy_j2objc.a", "dummy.o", ImmutableList.of( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "java/com/google/dummy/_j2objc/dummy/java/com/google/dummy/dummy.h", "java/com/google/dummy/_j2objc/dummy/java/com/google/dummy/dummy.m")); } // Tests that a j2objc library can acquire java library information from a skylark rule target. @Test public void testJ2ObjcLibraryDepThroughSkylarkRule() throws Exception { scratch.file("examples/inner.java"); scratch.file("examples/outer.java"); scratch.file( "examples/fake_rule.bzl", "def _fake_rule_impl(ctx):", " myProvider = ctx.attr.deps[0][JavaInfo]", " return struct(providers = [myProvider])", "", "fake_rule = rule(", " implementation = _fake_rule_impl,", " attrs = {'deps': attr.label_list()},", " provides = [JavaInfo],", ")"); scratch.file( "examples/BUILD", "package(default_visibility=['//visibility:public'])", "load('//examples:fake_rule.bzl', 'fake_rule')", "java_library(", " name = 'inner',", " srcs = ['inner.java'],", ")", "fake_rule(", " name = 'propagator',", " deps = [':inner'],", ")", "java_library(", " name = 'outer',", " srcs = ['outer.java'],", " deps = [':propagator'],", ")", "j2objc_library(", " name = 'transpile',", " deps = [", " ':outer',", " ],", ")", "objc_library(", " name = 'lib',", " srcs = ['lib.m'],", " deps = [':transpile'],", ")"); ConfiguredTarget objcTarget = getConfiguredTarget("//examples:lib"); ObjcProvider provider = objcTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); // The only way that //examples:lib can see inner's archive is through the skylark rule. assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .contains("examples/libinner_j2objc.a"); } @Test public void testJ2ObjcTranspiledHeaderInCompilationAction() throws Exception { scratch.file("app/lib.m"); scratch.file( "app/BUILD", "package(default_visibility=['//visibility:public'])", "objc_library(", " name = 'lib',", " srcs = ['lib.m'],", " deps = ['//java/com/google/dummy/test:transpile'],", ")"); checkObjcCompileActions( getBinArtifact("liblib.a", getConfiguredTarget("//app:lib")), "lib.o", ImmutableList.of( "java/com/google/dummy/test/_j2objc/test/java/com/google/dummy/test/test.h")); } @Test public void testProtoToolchainForJ2ObjcFlag() throws Exception { useConfiguration( "--proto_toolchain_for_java=//tools/proto/toolchains:java", "--proto_toolchain_for_j2objc=//tools/j2objc:alt_j2objc_proto_toolchain"); scratch.file("tools/j2objc/proto_plugin_binary"); scratch.file("tools/j2objc/alt_proto_runtime.h"); scratch.file("tools/j2objc/alt_proto_runtime.m"); scratch.file("tools/j2objc/some_blacklisted_proto.proto"); scratch.overwriteFile( "tools/j2objc/BUILD", "package(default_visibility=['//visibility:public'])", "exports_files(['j2objc_deploy.jar'])", "filegroup(", " name = 'j2objc_wrapper',", " srcs = ['j2objc_wrapper.py'],", ")", "filegroup(", " name = 'blacklisted_protos',", " srcs = ['some_blacklisted_proto.proto'],", ")", "filegroup(", " name = 'j2objc_header_map',", " srcs = ['j2objc_header_map.py'],", ")", "proto_lang_toolchain(", " name = 'alt_j2objc_proto_toolchain',", " command_line = '--PLUGIN_j2objc_out=file_dir_mapping,generate_class_mappings:$(OUT)',", " plugin = ':alt_proto_plugin',", " runtime = ':alt_proto_runtime',", " blacklisted_protos = [':blacklisted_protos'],", ")", "proto_library(", " name = 'blacklisted_proto_library',", " srcs = ['some_blacklisted_proto.proto'],", ")", "objc_library(", " name = 'alt_proto_runtime',", " hdrs = ['alt_proto_runtime.h'],", " srcs = ['alt_proto_runtime.m'],", ")", "filegroup(", " name = 'alt_proto_plugin',", " srcs = ['proto_plugin_binary']", ")"); scratch.file("java/com/google/dummy/test/proto/test.java"); scratch.file("java/com/google/dummy/test/proto/test.proto"); scratch.file( "java/com/google/dummy/test/proto/BUILD", "package(default_visibility=['//visibility:public'])", "proto_library(", " name = 'test_proto',", " srcs = ['test.proto'],", " deps = ['//tools/j2objc:blacklisted_proto_library'],", ")", "java_proto_library(", " name = 'test_java_proto',", " deps = [':test_proto'],", ")", "java_library(", " name = 'test',", " srcs = ['test.java'],", " deps = [':test_java_proto'])", "", "j2objc_library(", " name = 'transpile',", " deps = ['test'])"); ConfiguredTarget j2objcLibraryTarget = getConfiguredTarget("//java/com/google/dummy/test/proto:transpile"); ObjcProvider provider = j2objcLibraryTarget.get(ObjcProvider.SKYLARK_CONSTRUCTOR); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.LIBRARY))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/libjre_core_lib.a", "tools/j2objc/libalt_proto_runtime.a", "java/com/google/dummy/test/proto/libtest_j2objc.a", "java/com/google/dummy/test/proto/libtest_proto_j2objc.a"); assertThat(Artifact.toRootRelativePaths(provider.get(ObjcProvider.HEADER))) .containsExactly( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "third_party/java/j2objc/jre_core.h", "tools/j2objc/alt_proto_runtime.h", "java/com/google/dummy/test/proto/test.j2objc.pb.h", "java/com/google/dummy/test/proto/_j2objc/test/java/com/google/dummy/test/proto/test.h"); } @Test public void testJ2ObjcDeadCodeRemovalActionWithOptFlag() throws Exception { useConfiguration("--j2objc_dead_code_removal"); addSimpleJ2ObjcLibraryWithEntryClasses(); addSimpleBinaryTarget("//java/com/google/app/test:transpile"); ConfiguredTarget appTarget = getConfiguredTargetInAppleBinaryTransition("//app:app"); Artifact prunedArchive = getBinArtifact( "_j2objc_pruned/app/java/com/google/app/test/libtest_j2objc_pruned.a", appTarget); Action action = getGeneratingAction(prunedArchive); ConfiguredTarget javaTarget = getConfiguredTargetInAppleBinaryTransition("//java/com/google/app/test:test"); Artifact inputArchive = getBinArtifact("libtest_j2objc.a", javaTarget); Artifact headerMappingFile = getBinArtifact("test.mapping.j2objc", javaTarget); Artifact dependencyMappingFile = getBinArtifact("test.dependency_mapping.j2objc", javaTarget); Artifact archiveSourceMappingFile = getBinArtifact("test.archive_source_mapping.j2objc", javaTarget); String execPath = getConfiguration(javaTarget).getBinDirectory(RepositoryName.MAIN).getExecPath() + "/"; assertContainsSublist( ImmutableList.copyOf(paramFileArgsForAction(action)), new ImmutableList.Builder() .add("--input_archive") .add(inputArchive.getExecPathString()) .add("--output_archive") .add(prunedArchive.getExecPathString()) .add("--dummy_archive") .add( execPath + TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "tools/objc/libdummy_lib.a") .add("--xcrunwrapper") .add(MOCK_XCRUNWRAPPER_EXECUTABLE_PATH) .add("--dependency_mapping_files") .add(dependencyMappingFile.getExecPathString()) .add("--header_mapping_files") .add(headerMappingFile.getExecPathString()) .add("--archive_source_mapping_files") .add(archiveSourceMappingFile.getExecPathString()) .add("--entry_classes") .add("com.google.app.test.test") .build()); SpawnAction deadCodeRemovalAction = (SpawnAction) getGeneratingAction(prunedArchive); assertContainsSublist( deadCodeRemovalAction.getArguments(), new ImmutableList.Builder() .add( TestConstants.TOOLS_REPOSITORY_PATH_PREFIX + "tools/objc/j2objc_dead_code_pruner.py") .build()); assertThat(deadCodeRemovalAction.getOutputs()).containsExactly(prunedArchive); } /** Returns the actions created by the action template corresponding to given artifact. */ protected Iterable getActionsForInputsOfGeneratingActionTemplate( Artifact artifact, TreeFileArtifact treeFileArtifact) throws ActionTemplateExpansionException { CppCompileActionTemplate template = (CppCompileActionTemplate) getActionGraph().getGeneratingAction(artifact); return ImmutableList.builder() .addAll( template.generateActionForInputArtifacts( ImmutableList.of(treeFileArtifact), ArtifactOwner.NullArtifactOwner.INSTANCE)) .build(); } @Test public void testCompileActionTemplateFromGenJar() throws Exception { useConfiguration("--cpu=ios_i386", "--ios_minimum_os=1.0"); addSimpleJ2ObjcLibraryWithJavaPlugin(); Artifact archive = j2objcArchive("//java/com/google/app/test:transpile", "test"); CommandAction archiveAction = (CommandAction) getGeneratingAction(archive); Artifact objectFilesFromGenJar = getFirstArtifactEndingWith(archiveAction.getInputs(), "source_files"); assertThat(objectFilesFromGenJar.isTreeArtifact()).isTrue(); assertThat(objectFilesFromGenJar.getRootRelativePathString()) .isEqualTo("java/com/google/app/test/_objs/test/non_arc/source_files"); ActionAnalysisMetadata actionTemplate = getActionGraph().getGeneratingAction(objectFilesFromGenJar); Artifact sourceFilesFromGenJar = getFirstArtifactEndingWith(actionTemplate.getInputs(), "source_files"); Artifact headerFilesFromGenJar = getFirstArtifactEndingWith(actionTemplate.getInputs(), "header_files"); assertThat(sourceFilesFromGenJar.getRootRelativePathString()) .isEqualTo("java/com/google/app/test/_j2objc/src_jar_files/test/source_files"); assertThat(headerFilesFromGenJar.getRootRelativePathString()) .isEqualTo("java/com/google/app/test/_j2objc/src_jar_files/test/header_files"); // The files contained inside the tree artifacts are not known until execution time. // Therefore we need to fake some files inside them to test the action template in this // analysis-time test. TreeFileArtifact oneSourceFileFromGenJar = ActionInputHelper.treeFileArtifact((SpecialArtifact) sourceFilesFromGenJar, "children1.m"); TreeFileArtifact oneObjFileFromGenJar = ActionInputHelper.treeFileArtifact((SpecialArtifact) objectFilesFromGenJar, "children1.o"); Iterable compileActions = getActionsForInputsOfGeneratingActionTemplate( objectFilesFromGenJar, oneSourceFileFromGenJar); CommandAction compileAction = Iterables.getOnlyElement(compileActions); ConfiguredTarget j2objcLibraryTarget = getConfiguredTarget("//java/com/google/dummy/test:transpile"); String genfilesFragment = getConfiguration(j2objcLibraryTarget).getGenfilesFragment().toString(); String binFragment = getConfiguration(j2objcLibraryTarget).getBinFragment().toString(); AppleConfiguration appleConfiguration = getConfiguration(j2objcLibraryTarget).getFragment(AppleConfiguration.class); String commandLine = Joiner.on(" ").join(compileAction.getArguments()); ImmutableList expectedArgs = new ImmutableList.Builder() .addAll(AppleToolchain.DEFAULT_WARNINGS.values()) .add("-fexceptions") .add("-fasm-blocks") .add("-fobjc-abi-version=2") .add("-fobjc-legacy-dispatch") .add("-DOS_IOS") .add("-mios-simulator-version-min=1.0") .add("-arch", "i386") .add("-isysroot") .add(AppleToolchain.sdkDir()) .add("-F") .add(AppleToolchain.sdkDir() + "/Developer/Library/Frameworks") .add("-F") .add(AppleToolchain.platformDeveloperFrameworkDir(appleConfiguration)) .add("-O0") .add("-DDEBUG=1") .add("-iquote") .add(".") .add("-iquote") .add(genfilesFragment) .add("-I") .add(binFragment + "/java/com/google/app/test/_j2objc/test") .add("-I") .add(headerFilesFromGenJar.getExecPathString()) .add("-fno-strict-overflow") .add("-fno-objc-arc") .add("-c") .add(oneSourceFileFromGenJar.getExecPathString()) .add("-o") .add(oneObjFileFromGenJar.getExecPathString()) .build(); for (String expectedArg : expectedArgs) { assertThat(commandLine).contains(expectedArg); } } @Test public void testModuleMapsArePropagatedStrictly() throws Exception { useConfiguration( "--experimental_objc_enable_module_maps", "--incompatible_strict_objc_module_maps"); scratch.file("java/com/google/transpile/dummy.java"); scratch.file( "java/com/google/transpile/BUILD", "package(default_visibility=['//visibility:public'])", "java_library(", " name = 'dummy1',", " srcs = ['dummy.java'],", ")", "java_library(", " name = 'dummy2',", " srcs = ['dummy.java'],", ")", "java_library(", " name = 'dummy3',", " srcs = ['dummy.java'], deps = [':dummy2'],", ")", "j2objc_library(", " name = 'lib1',", " deps = [':dummy1'],", ")", "j2objc_library(", " name = 'lib2',", " deps = [':lib1', ':dummy3'],", ")"); // Bazel doesn't give us a way to test the aspect directly on the java_library targets, so we // can only test propagation through the j2objc_libraries that attach the aspect. // lib1 should propagate the module map from its java_library dependency. assertThat( getFirstPropagatedModuleMap( "//java/com/google/transpile:lib1", "dummy1.modulemaps/module.modulemap")) .isNotNull(); // lib2 should propagate the module maps from its transitive java_library dependencies... assertThat( getFirstPropagatedModuleMap( "//java/com/google/transpile:lib2", "dummy2.modulemaps/module.modulemap")) .isNotNull(); assertThat( getFirstPropagatedModuleMap( "//java/com/google/transpile:lib2", "dummy3.modulemaps/module.modulemap")) .isNotNull(); // ...but it should not propagate the module maps from its j2objc_library dependencies. assertThat( getFirstPropagatedModuleMap( "//java/com/google/transpile:lib2", "dummy1.modulemaps/module.modulemap")) .isNull(); } private Artifact getFirstPropagatedModuleMap(String label, String nameSuffix) throws Exception { ObjcProvider provider = providerForTarget(label); // The ObjC and Swift build rules retrieve the module maps they need to pass to the compiler by // building a transitive provider from the target-to-build's deps. We duplicate that behavior // here to make sure we're testing the provider set that the eventual target library would see. ObjcProvider newProvider = new ObjcProvider.Builder(SkylarkSemantics.DEFAULT_SEMANTICS) .addTransitiveAndPropagate(ImmutableList.of(provider)) .build(); return getFirstArtifactEndingWith(newProvider.get(ObjcProvider.MODULE_MAP), nameSuffix); } }