// 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.workspace;
import com.google.common.collect.Lists;
import com.google.common.io.Files;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.StoredEventHandler;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.unix.UnixFileSystem;
import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.JavaIoFileSystem;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.workspace.maven.DefaultModelResolver;
import com.google.devtools.build.workspace.maven.Resolver;
import com.google.devtools.common.options.OptionsParser;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Generates a WORKSPACE file for Bazel from other types of dependency trackers.
*/
public class GenerateWorkspace {
private final EventHandler handler;
private final FileSystem fileSystem;
private final Resolver resolver;
private final List<String> inputs;
private final Path outputDir;
public static void main(String[] args) throws InterruptedException {
OptionsParser parser = OptionsParser.newOptionsParser(GenerateWorkspaceOptions.class);
parser.parseAndExitUponError(args);
GenerateWorkspaceOptions options = parser.getOptions(GenerateWorkspaceOptions.class);
if (options.mavenProjects.isEmpty()
&& options.bazelProjects.isEmpty()
&& options.artifacts.isEmpty()) {
printUsage(parser);
return;
}
GenerateWorkspace workspaceFileGenerator = new GenerateWorkspace(options.outputDir);
workspaceFileGenerator.generateFromWorkspace(options.bazelProjects);
workspaceFileGenerator.generateFromPom(options.mavenProjects);
workspaceFileGenerator.generateFromArtifacts(options.artifacts);
if (!workspaceFileGenerator.hasErrors()) {
workspaceFileGenerator.writeResults();
}
workspaceFileGenerator.cleanup();
if (workspaceFileGenerator.hasErrors()) {
System.exit(1);
}
}
private static void printUsage(OptionsParser parser) {
System.out.println("Usage: generate_workspace (-b PATH|-m PATH|-a coord)+ [-o PATH]\n\n"
+ "Generates a WORKSPACE file from the given projects and a BUILD file with a rule that "
+ "contains all of the transitive dependencies. At least one bazel_project, "
+ "maven_project, or artifact coordinate must be specified. If output_dir is not "
+ "specified, the generated files will be written to a temporary directory.\n");
System.out.println(parser.describeOptions(Collections.<String, String>emptyMap(),
OptionsParser.HelpVerbosity.LONG));
}
private GenerateWorkspace(String outputDir) {
this.handler = new EventHandler();
this.fileSystem = getFileSystem();
this.resolver = new Resolver(handler, new DefaultModelResolver());
this.inputs = Lists.newArrayList();
if (outputDir.isEmpty()) {
this.outputDir = fileSystem.getPath(Files.createTempDir().toString());
} else {
this.outputDir = fileSystem.getPath(outputDir);
}
}
static FileSystem getFileSystem() {
return OS.getCurrent() == OS.WINDOWS
? new JavaIoFileSystem() : new UnixFileSystem();
}
private void generateFromWorkspace(List<String> projects) throws InterruptedException {
for (String project : projects) {
WorkspaceResolver workspaceResolver = new WorkspaceResolver(resolver, handler);
Path projectPath = fileSystem.getPath(getAbsolute(project)).getRelative("WORKSPACE");
Package externalPackage = workspaceResolver.parse(projectPath);
inputs.add(projectPath.getPathString());
workspaceResolver.resolveTransitiveDependencies(externalPackage);
}
}
private void generateFromPom(List<String> projects) {
for (String project : projects) {
String pomFile = resolver.resolvePomDependencies(getAbsolute(project));
if (pomFile != null) {
inputs.add(pomFile);
}
}
}
private void generateFromArtifacts(List<String> artifacts) {
for (String artifactCoord : artifacts) {
inputs.add(artifactCoord);
resolver.resolveArtifact(artifactCoord);
}
}
private String getAbsolute(String path) {
return Paths.get(System.getProperty("user.dir")).resolve(path).toString();
}
/**
* Returns if there were any errors generating the WORKSPACE and BUILD files.
*/
private boolean hasErrors() {
return handler.hasErrors();
}
private void writeResults() {
String date = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
File workspaceFile = outputDir.getRelative("WORKSPACE").getPathFile();
File buildFile = outputDir.getRelative("BUILD").getPathFile();
// Don't overwrite existing files with generated ones.
if (workspaceFile.exists()) {
workspaceFile = outputDir.getRelative(date + ".WORKSPACE").getPathFile();
}
if (buildFile.exists()) {
buildFile = outputDir.getRelative(date + ".BUILD").getPathFile();
}
try (PrintStream workspaceStream = new PrintStream(workspaceFile);
PrintStream buildStream = new PrintStream(buildFile)) {
ResultWriter writer = new ResultWriter(inputs, resolver.getRules());
writer.writeWorkspace(workspaceStream);
writer.writeBuild(buildStream);
} catch (IOException e) {
handler.handle(Event.error(
"Could not write WORKSPACE and BUILD files to " + outputDir + ": " + e.getMessage()));
return;
}
System.err.println("Wrote:\n" + workspaceFile + "\n" + buildFile);
}
private void cleanup() {
for (Event event : handler.getEvents()) {
System.err.println(event);
}
}
private class EventHandler extends StoredEventHandler {
@Override
public void handle(Event event) {
System.err.println(event.getKind() + ": " + event.getMessage());
}
}
}