| /* |
| * Copyright (c) 2011 Google Inc. |
| * |
| * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse |
| * Public License v1.0 which accompanies this distribution, and is available at |
| * |
| * http://www.eclipse.org/legal/epl-v10.html |
| */ |
| package com.google.eclipse.protobuf.ui.builder.protoc; |
| |
| import static com.google.common.collect.Lists.newArrayList; |
| import static com.google.common.io.Closeables.closeQuietly; |
| import static com.google.eclipse.protobuf.ui.builder.protoc.ConsolePrinter.createAndDisplayConsole; |
| import static com.google.eclipse.protobuf.ui.exception.CoreExceptions.error; |
| import static com.google.eclipse.protobuf.ui.preferences.compiler.core.CompilerPreferences.compilerPreferences; |
| import static com.google.eclipse.protobuf.ui.util.CommaSeparatedValues.splitCsv; |
| import static java.util.Collections.*; |
| import static org.eclipse.core.resources.IResource.DEPTH_INFINITE; |
| |
| import java.io.*; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.xtext.builder.IXtextBuilderParticipant; |
| import org.eclipse.xtext.resource.IResourceDescription; |
| import org.eclipse.xtext.resource.IResourceDescription.Delta; |
| import org.eclipse.xtext.ui.editor.preferences.IPreferenceStoreAccess; |
| |
| import com.google.eclipse.protobuf.ui.preferences.compiler.core.CompilerPreferences; |
| import com.google.eclipse.protobuf.ui.preferences.paths.core.DirectoryPath; |
| import com.google.eclipse.protobuf.ui.preferences.paths.core.PathsPreferences; |
| import com.google.inject.Inject; |
| |
| /** |
| * Calls protoc to generate Java, C++ or Python code from .proto files. |
| * |
| * @author alruiz@google.com (Alex Ruiz) |
| */ |
| public class ProtobufBuildParticipant implements IXtextBuilderParticipant { |
| @Inject private ProtocCommandFactory commandFactory; |
| @Inject private ProtocOutputParser outputParser; |
| @Inject private ProtoDescriptorPathFinder protoDescriptorPathFinder; |
| @Inject private IPreferenceStoreAccess storeAccess; |
| |
| @Override public void build(IBuildContext context, IProgressMonitor monitor) throws CoreException { |
| IProject project = context.getBuiltProject(); |
| CompilerPreferences preferences = compilerPreferences(storeAccess, project); |
| if (!preferences.compileProtoFiles().getValue()) { |
| return; |
| } |
| List<Delta> deltas = context.getDeltas(); |
| if (deltas.isEmpty()) { |
| return; |
| } |
| OutputDirectories outputDirectories = new OutputDirectories(project, preferences); |
| String descriptorPath = descriptorPath(preferences); |
| List<String> importRoots = importRoots(project); |
| for (Delta d : deltas) { |
| IFile source = protoFile(d.getNew(), project); |
| if (source == null) { |
| continue; |
| } |
| if (importRoots.isEmpty()) { |
| importRoots = singleImportRoot(source); |
| } |
| generateSingleProto(source, protocPath(preferences), importRoots, descriptorPath, outputDirectories); |
| } |
| if (preferences.refreshResources().getValue()) { |
| boolean refreshProject = preferences.refreshProject().getValue(); |
| refresh(project, outputDirectories, refreshProject, monitor); |
| } |
| } |
| |
| private String descriptorPath(CompilerPreferences preferences) { |
| return protoDescriptorPathFinder.findRootOf(preferences.descriptorPath().getValue()); |
| } |
| |
| private List<String> importRoots(IProject project) { |
| List<String> paths = newArrayList(); |
| PathsPreferences preferences = new PathsPreferences(storeAccess, project); |
| if (preferences.filesInMultipleDirectories().getValue()) { |
| String directoryPaths = preferences.directoryPaths().getValue(); |
| for (String importRoot : splitCsv(directoryPaths)) { |
| DirectoryPath path = DirectoryPath.parse(importRoot, project); |
| String location = path.absolutePathInFileSystem(); |
| if (location != null) { |
| paths.add(location); |
| } |
| } |
| return unmodifiableList(paths); |
| } |
| return emptyList(); |
| } |
| |
| private IFile protoFile(IResourceDescription resource, IProject project) { |
| String path = filePathIfIsProtoFile(resource); |
| return (path == null) ? null : project.getWorkspace().getRoot().getFile(new Path(path)); |
| } |
| |
| private String filePathIfIsProtoFile(IResourceDescription resource) { |
| if (resource == null) { |
| return null; |
| } |
| URI uri = resource.getURI(); |
| if (!uri.fileExtension().equals("proto")) |
| { |
| return null; |
| } |
| if (uri.scheme() == null) { |
| return uri.toFileString(); |
| } |
| StringBuilder b = new StringBuilder(); |
| int segmentCount = uri.segmentCount(); |
| for (int i = 1; i < segmentCount; i++) |
| { |
| b.append("/").append(uri.segment(i)); |
| } |
| return b.length() == 0 ? null : b.toString(); |
| } |
| |
| private List<String> singleImportRoot(IFile source) { |
| IProject project = source.getProject(); |
| File projectFile = project.getLocation().toFile(); |
| File current = source.getLocation().toFile(); |
| while (!current.getParentFile().equals(projectFile)) { |
| current = current.getParentFile(); |
| } |
| return singletonList(current.toString()); |
| } |
| |
| String protocPath(CompilerPreferences preferences) { |
| if (preferences.useProtocInSystemPath().getValue()) { |
| return "protoc"; |
| } |
| return preferences.protocPath().getValue(); |
| } |
| |
| private void generateSingleProto(IFile source, String protocPath, List<String> importRoots, String descriptorPath, |
| OutputDirectories outputDirectories) throws CoreException { |
| String command = commandFactory.protocCommand(source, protocPath, importRoots, descriptorPath, outputDirectories); |
| ConsolePrinter console = null; |
| try { |
| console = createAndDisplayConsole(); |
| console.printCommand(command); |
| Process process = Runtime.getRuntime().exec(command); |
| processStream(process.getErrorStream(), source, console); |
| process.destroy(); |
| } catch (Throwable e) { |
| e.printStackTrace(); |
| throw error(e); |
| } finally { |
| if (console != null) { |
| console.close(); |
| } |
| } |
| } |
| |
| private void processStream(InputStream stream, IFile source, ConsolePrinter console) throws Throwable { |
| InputStreamReader reader = null; |
| try { |
| reader = new InputStreamReader(stream); |
| BufferedReader bufferedReader = new BufferedReader(reader); |
| String line = null; |
| ProtocMarkerFactory markerFactory = new ProtocMarkerFactory(source); |
| while ((line = bufferedReader.readLine()) != null) { |
| outputParser.parseAndAddMarkerIfNecessary(line, markerFactory); |
| console.printProtocOutput(line); |
| } |
| } finally { |
| closeQuietly(reader); |
| } |
| } |
| |
| private void refresh(IProject project, OutputDirectories outputDirectories, boolean refreshProject, |
| IProgressMonitor monitor) throws CoreException { |
| if (refreshProject) { |
| project.refreshLocal(DEPTH_INFINITE, monitor); |
| return; |
| } |
| refresh(outputDirectories.java(), monitor); |
| refresh(outputDirectories.cpp(), monitor); |
| refresh(outputDirectories.python(), monitor); |
| } |
| |
| private void refresh(OutputDirectory directory, IProgressMonitor monitor) throws CoreException { |
| if (directory.isEnabled()) { |
| IFolder location = directory.getLocation(); |
| location.refreshLocal(DEPTH_INFINITE, monitor); |
| } |
| } |
| } |