Skip to content

Commit

Permalink
Merge pull request gradle#8899 from gradle/lacasseio/native/force-x86…
Browse files Browse the repository at this point in the history
…-on-older-xcode

Support C++ compilation with multiple Xcode version
  • Loading branch information
big-guy authored Apr 16, 2019
2 parents 9189314 + 5fda383 commit b89cde1
Showing 1 changed file with 150 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.gradle.nativeplatform.fixtures;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
Expand Down Expand Up @@ -53,10 +54,15 @@
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static org.gradle.nativeplatform.fixtures.msvcpp.VisualStudioVersion.VISUALSTUDIO_2012;
import static org.gradle.nativeplatform.fixtures.msvcpp.VisualStudioVersion.VISUALSTUDIO_2013;
Expand Down Expand Up @@ -128,21 +134,27 @@ public static List<ToolChainCandidate> getToolChains() {
}

static private List<ToolChainCandidate> findClangs(boolean mustFind) {
GccMetadataProvider versionDeterminer = GccMetadataProvider.forClang(TestFiles.execActionFactory());

Set<File> clangCandidates = ImmutableSet.copyOf(OperatingSystem.current().findAllInPath("clang"));
List<ToolChainCandidate> toolChains = Lists.newArrayList();
if (!clangCandidates.isEmpty()) {
File firstInPath = clangCandidates.iterator().next();
for (File candidate : clangCandidates) {
SearchResult<GccMetadata> version = versionDeterminer.getCompilerMetaData(Collections.emptyList(), spec -> spec.executable(candidate));
if (version.isAvailable()) {
InstalledClang clang = new InstalledClang(version.getComponent().getVersion());
if (!candidate.equals(firstInPath)) {
// Not the first g++ in the path, needs the path variable updated
clang.inPath(candidate.getParentFile());

// On macOS, we assume co-located Xcode is installed into /opt/xcode and default location at /Applications/Xcode.app
// We need to search for Clang differently on macOS because we need to know the Xcode version for x86 support.
if (OperatingSystem.current().isMacOsX()) {
toolChains.addAll(findXcodes().stream().map(InstalledXcode::getClang).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()));
} else {
GccMetadataProvider versionDeterminer = GccMetadataProvider.forClang(TestFiles.execActionFactory());
Set<File> clangCandidates = ImmutableSet.copyOf(OperatingSystem.current().findAllInPath("clang"));
if (!clangCandidates.isEmpty()) {
File firstInPath = clangCandidates.iterator().next();
for (File candidate : clangCandidates) {
SearchResult<GccMetadata> version = versionDeterminer.getCompilerMetaData(Collections.emptyList(), spec -> spec.executable(candidate));
if (version.isAvailable()) {
InstalledClang clang = new InstalledClang(version.getComponent().getVersion());
if (!candidate.equals(firstInPath)) {
// Not the first g++ in the path, needs the path variable updated
clang.inPath(candidate.getParentFile());
}
toolChains.add(clang);
}
toolChains.add(clang);
}
}
}
Expand Down Expand Up @@ -271,17 +283,8 @@ public boolean accept(File swiftInstall) {
}
}

// On macOS, we assume co-located Xcode is installed into /opt/xcode
File rootXcodeInstall = new File("/opt/xcode");
File[] xcodeCandidates = GUtil.elvis(rootXcodeInstall.listFiles(xcodeInstall -> xcodeInstall.isDirectory()), new File[0]);
for (File xcodeInstall : xcodeCandidates) {
File swiftc = new File("/usr/bin/swiftc");
SearchResult<SwiftcMetadata> version = versionDeterminer.getCompilerMetaData(Collections.emptyList(), spec -> spec.executable(swiftc).environment("DEVELOPER_DIR", xcodeInstall.getAbsolutePath()));
if (version.isAvailable()) {
File binDir = swiftc.getParentFile();
toolChains.add(new InstalledXcode(xcodeInstall, version.getComponent().getVersion()).inPath(binDir, new File("/usr/bin")));
}
}
// On macOS, we assume co-located Xcode is installed into /opt/xcode and default location at /Applications/Xcode.app
toolChains.addAll(findXcodes().stream().map(InstalledXcode::getSwiftc).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()));

List<File> swiftcCandidates = OperatingSystem.current().findAllInPath("swiftc");
for (File candidate : swiftcCandidates) {
Expand Down Expand Up @@ -732,47 +735,150 @@ public boolean meets(ToolChainRequirement requirement) {
}
}

public static class InstalledXcode extends InstalledSwiftc {
static List<InstalledXcode> findXcodes() {
List<InstalledXcode> xcodes = new ArrayList<>();

// On macOS, we assume co-located Xcode is installed into /opt/xcode
File rootXcodeInstall = new File("/opt/xcode");
List<File> xcodeCandidates = Lists.newArrayList(Arrays.asList(GUtil.elvis(rootXcodeInstall.listFiles(xcodeInstall -> xcodeInstall.isDirectory()), new File[0])));
xcodeCandidates.add(new File("/Applications/Xcode.app")); // Default Xcode installation
xcodeCandidates.stream().filter(File::exists).forEach(xcodeInstall -> {
TestFile xcodebuild = new TestFile("/usr/bin/xcodebuild");

String output = xcodebuild.execute(Collections.singletonList("-version"), Collections.singletonList("DEVELOPER_DIR=" + xcodeInstall.getAbsolutePath())).getOut();
Pattern versionRegex = Pattern.compile("Xcode (\\d+\\.\\d+(\\.\\d+)?)");
Matcher matcher = versionRegex.matcher(output);
if (matcher.find()) {
VersionNumber version = VersionNumber.parse(matcher.group(1));
xcodes.add(new InstalledXcode(xcodeInstall, version));
}
});

return xcodes;
}

public static class InstalledXcode {
private static final ProcessEnvironment PROCESS_ENVIRONMENT = NativeServicesTestFixture.getInstance().get(ProcessEnvironment.class);
private final File xcodeDir;
private final VersionNumber version;
private String originalDeveloperDir;

public InstalledXcode(File xcodeDir, VersionNumber compilerVersion) {
super(new File("/usr/bin"), compilerVersion);
public InstalledXcode(File xcodeDir, VersionNumber version) {
this.xcodeDir = xcodeDir;
this.version = version;
}

@Override
public List<String> getRuntimeEnv() {
List<String> result = new ArrayList<>();
result.addAll(super.getRuntimeEnv());
result.add("DEVELOPER_DIR=" + xcodeDir.getAbsolutePath());
return result;
private List<String> getRuntimeEnv() {
return ImmutableList.of("DEVELOPER_DIR=" + xcodeDir.getAbsolutePath());
}

@Override
public void initialiseEnvironment() {
super.initialiseEnvironment();

private void initialiseEnvironment() {
originalDeveloperDir = System.getenv("DEVELOPER_DIR");
System.out.println(String.format("Using DEVELOPER_DIR %s", xcodeDir.getAbsolutePath()));
PROCESS_ENVIRONMENT.setEnvironmentVariable("DEVELOPER_DIR", xcodeDir.getAbsolutePath());
}

@Override
public void resetEnvironment() {
private void resetEnvironment() {
if (originalDeveloperDir != null) {
PROCESS_ENVIRONMENT.setEnvironmentVariable("DEVELOPER_DIR", xcodeDir.getAbsolutePath());
}

super.resetEnvironment();
}

@Override
public void configureExecuter(GradleExecuter executer) {
super.configureExecuter(executer);
private void configureExecuter(GradleExecuter executer) {
executer.withEnvironmentVars(ImmutableMap.of("DEVELOPER_DIR", xcodeDir.getAbsolutePath()));
}

public Optional<InstalledToolChain> getSwiftc() {
SwiftcMetadataProvider versionDeterminer = new SwiftcMetadataProvider(TestFiles.execActionFactory());
File swiftc = new File("/usr/bin/swiftc");
SearchResult<SwiftcMetadata> version = versionDeterminer.getCompilerMetaData(Collections.emptyList(), spec -> spec.executable(swiftc).environment("DEVELOPER_DIR", xcodeDir.getAbsolutePath()));
if (!version.isAvailable()) {
return Optional.empty();
}

return Optional.of(new InstalledSwiftc(new File("/usr/bin"), version.getComponent().getVersion()) {
@Override
public List<String> getRuntimeEnv() {
List<String> result = new ArrayList<>();
result.addAll(super.getRuntimeEnv());
result.addAll(InstalledXcode.this.getRuntimeEnv());
return result;
}

@Override
public void initialiseEnvironment() {
super.initialiseEnvironment();
InstalledXcode.this.initialiseEnvironment();
}

@Override
public void resetEnvironment() {
InstalledXcode.this.resetEnvironment();
super.resetEnvironment();
}

@Override
public void configureExecuter(GradleExecuter executer) {
super.configureExecuter(executer);
InstalledXcode.this.configureExecuter(executer);
}

@Override
public String getInstanceDisplayName() {
return String.format("%s from Xcode '%s'", super.getInstanceDisplayName(), InstalledXcode.this.version.toString());
}
});
}

public Optional<InstalledToolChain> getClang() {
GccMetadataProvider versionDeterminer = GccMetadataProvider.forClang(TestFiles.execActionFactory());
File clang = new File("/usr/bin/clang");
SearchResult<GccMetadata> version = versionDeterminer.getCompilerMetaData(Collections.emptyList(), spec -> spec.executable(clang).environment("DEVELOPER_DIR", xcodeDir.getAbsolutePath()));
if (!version.isAvailable()) {
return Optional.empty();
}

return Optional.of(new InstalledClang(version.getComponent().getVersion()) {
@Override
public List<String> getRuntimeEnv() {
List<String> result = new ArrayList<>();
result.addAll(super.getRuntimeEnv());
result.addAll(InstalledXcode.this.getRuntimeEnv());
return result;
}

@Override
public void initialiseEnvironment() {
super.initialiseEnvironment();
InstalledXcode.this.initialiseEnvironment();
}

@Override
public void resetEnvironment() {
InstalledXcode.this.resetEnvironment();
super.resetEnvironment();
}

@Override
public void configureExecuter(GradleExecuter executer) {
super.configureExecuter(executer);
InstalledXcode.this.configureExecuter(executer);
}

@Override
public boolean meets(ToolChainRequirement requirement) {
if (ToolChainRequirement.SUPPORTS_32.equals(requirement) || ToolChainRequirement.SUPPORTS_32_AND_64.equals(requirement)) {
return InstalledXcode.this.version.getMajor() < 10;
}
return super.meets(requirement);
}

@Override
public String getInstanceDisplayName() {
return String.format("%s from Xcode '%s'", super.getInstanceDisplayName(), InstalledXcode.this.version.toString());
}
});
}
}

public static class InstalledVisualCpp extends InstalledToolChain {
Expand Down

0 comments on commit b89cde1

Please sign in to comment.