blob: e3576c226f2bd0ca21c7f436b524c8ec3a94b58c [file] [log] [blame]
// Copyright 2014 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.xcode.xcodegen;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.Control;
import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.TargetControl;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.Options;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.OptionsParsingException;
import com.facebook.buck.apple.xcode.xcodeproj.PBXProject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.List;
/**
* Entry-point for the command-line Xcode project generator.
*/
public class XcodeGen {
/**
* Options for {@link XcodeGen}.
*/
public static class XcodeGenOptions extends OptionsBase {
@Option(
name = "control",
help = "Path to a control file, which contains only a binary serialized instance of "
+ "the Control protocol buffer. Required.",
defaultValue = "null")
public String control;
}
public static void main(String[] args) throws IOException, OptionsParsingException {
OptionsParser parser = OptionsParser.newOptionsParser(XcodeGenOptions.class);
parser.parse(args);
XcodeGenOptions options = parser.getOptions(XcodeGenOptions.class);
if (options.control == null) {
throw new IllegalArgumentException("--control must be specified\n"
+ Options.getUsage(XcodeGenOptions.class));
}
FileSystem fileSystem = FileSystems.getDefault();
Control controlPb;
try (InputStream in = Files.newInputStream(fileSystem.getPath(options.control))) {
controlPb = Control.parseFrom(in);
}
Path pbxprojPath = fileSystem.getPath(controlPb.getPbxproj());
Iterator<String> srcList = allSourceFilePaths(controlPb).iterator();
Path workspaceRoot;
// TODO(bazel-team): Remove this if-else clause once Bazel passes in the workspace root.
if (controlPb.hasWorkspaceRoot()) {
workspaceRoot = fileSystem.getPath(controlPb.getWorkspaceRoot());
} else if (!srcList.hasNext()) {
workspaceRoot = XcodeprojGeneration.relativeWorkspaceRoot(pbxprojPath);
} else {
// Get the absolute path to the workspace root.
// TODO(bazel-team): Remove this hack, possibly by converting Xcodegen to be run with
// "bazel run" and using RUNFILES to get the workspace root. For now, this is needed to work
// around Xcode's handling of symlinks not playing nicely with how Bazel stores output
// artifacts in /private/var/tmp. This means a relative path from .xcodeproj in bazel-out to
// the workspace root in .xcodeproj will not work properly at certain times during
// Xcode/xcodebuild execution. Walking up the path of a known source file prevents having
// to reason about a file that might only be accessible through a symlink, like a tools jar.
Path relSourceFilePath = fileSystem.getPath(srcList.next());
Path absSourceFilePath = relSourceFilePath.toAbsolutePath();
workspaceRoot = absSourceFilePath;
for (int i = 0; i < relSourceFilePath.getNameCount(); i++) {
workspaceRoot = workspaceRoot.getParent();
}
}
try (OutputStream out = Files.newOutputStream(pbxprojPath)) {
// This workspace root here is relative to the PWD, so that the .xccurrentversion
// files can actually be read. The other workspaceRoot is relative to the .xcodeproj
// root or is absolute.
Path relativeWorkspaceRoot = fileSystem.getPath(".");
PBXProject project = XcodeprojGeneration.xcodeproj(
workspaceRoot, controlPb,
ImmutableList.of(
new CurrentVersionSetter(relativeWorkspaceRoot),
new PbxReferencesGrouper(fileSystem)));
XcodeprojGeneration.write(out, project);
}
}
private static Iterable<String> allSourceFilePaths(Control control) {
return Iterables.concat(
Iterables.transform(control.getTargetList(),
new Function<TargetControl, List<String>>() {
public List<String> apply(TargetControl tc) {
return tc.getSourceFileList();
}
}));
}
}