/* * Copyright (c) 2009-2010, IETR/INSA of Rennes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of the IETR/INSA of Rennes nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ package net.sf.orcc.frontend; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import net.sf.orcc.cal.CalStandaloneSetup; import net.sf.orcc.cal.cal.AstEntity; import net.sf.orcc.cal.cal.Import; import net.sf.orcc.cal.generator.CalGenerator; import net.sf.orcc.df.util.XdfConstants; import net.sf.orcc.util.CommandLineUtil; import net.sf.orcc.util.DomUtil; import net.sf.orcc.util.OrccLogger; import net.sf.orcc.util.OrccUtil; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Appender; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Layout; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.xtext.generator.IGenerator; import org.eclipse.xtext.generator.JavaIoFileSystemAccess; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.inject.Injector; /** * This class defines an RVC-CAL command line version of the frontend. It should * be used with the following command-line, when all plugins are installed in * eclipse : * * <pre> * eclipse -application net.sf.orcc.cal.cli -data <workspacePath> <projectName> [<network>] * </pre> * * @author Matthieu Wipliez * @author Antoine Lorence * */ public class FrontendCli implements IApplication { // Display the command line usage of this application private final String USAGE = "Usage : \n" + "net.sf.orcc.cal.cli <project> [<network>]"; private final ResourceSet resourceSet; private final IWorkspace workspace; private boolean wasAutoBuildEnabled; private IProject project; private IFile networkFile; final Injector injector; public FrontendCli() { // Configure Log4j to output logs in System.out console (used in Xtext // framework) final Layout l = new PatternLayout( PatternLayout.TTCC_CONVERSION_PATTERN); final Appender a = new ConsoleAppender(l, ConsoleAppender.SYSTEM_OUT); Logger.getRootLogger().addAppender(a); // Default level to Info, reduces amount of messages displayed Logger.getRootLogger().setLevel(Level.INFO); injector = new CalStandaloneSetup() .createInjectorAndDoEMFRegistration(); workspace = ResourcesPlugin.getWorkspace(); wasAutoBuildEnabled = false; project = null; networkFile = null; // Get the resource set used by Frontend resourceSet = injector.getInstance(ResourceSet.class); } @Override public Object start(IApplicationContext context) { final String[] args = (String[]) context.getArguments().get( IApplicationContext.APPLICATION_ARGS); if (!parseCommandLine(args)) { // parseCommandLine() already displayed an error message before // returning false return IApplication.EXIT_RELAUNCH; } // The IR generation process starts now try { // IMPORTANT : Disable auto-building, because it requires Xtext UI // plugins to be launched wasAutoBuildEnabled = CommandLineUtil.disableAutoBuild(workspace); // Get the projects to compile in the right order OrccLogger.traceln("Setup " + project.getName() + " as working project"); final Collection<IProject> orderedProjects = getOrderedProjects(project); // Check for missing output folders in project for (final IProject proj : orderedProjects) { final IFolder outDir = OrccUtil.getOutputFolder(proj); if (!outDir.exists()) { outDir.create(true, true, new NullProgressMonitor()); } } final Multimap<IProject, Resource> resourcesMap = HashMultimap .create(); // Classical full build, store all files for each projects if (networkFile == null) { for (final IProject project : orderedProjects) { // Get all CAL files of the project final List<IFile> files = OrccUtil.getAllFiles( OrccUtil.CAL_SUFFIX, OrccUtil.getSourceFolders(project)); // Store these files to build them later for (final IFile file : files) { resourcesMap.put(project, getResource(file)); } } } // Specific build. We only write IR needed to build the network // referenced on command line else { // Compute the list of all xdf and cal files in required // projects. Store all these files // in a map, indexed by their qualified name final Map<String, IFile> allFiles = new HashMap<String, IFile>(); for (final IProject project : orderedProjects) { final List<IFolder> folders = OrccUtil .getAllSourceFolders(project); final List<IFile> networks = OrccUtil.getAllFiles( OrccUtil.NETWORK_SUFFIX, folders); for (final IFile file : networks) { allFiles.put(OrccUtil.getQualifiedName(file), file); } final List<IFile> entities = OrccUtil.getAllFiles( OrccUtil.CAL_SUFFIX, folders); for (final IFile file : entities) { allFiles.put(OrccUtil.getQualifiedName(file), file); } } storeReferencedEntities(networkFile, allFiles, resourcesMap); } final CalGenerator calGenerator = (CalGenerator) injector .getInstance(IGenerator.class); final JavaIoFileSystemAccess fsa = injector .getInstance(JavaIoFileSystemAccess.class); for (final IProject project : orderedProjects) { OrccLogger.traceln("+-------------------"); OrccLogger.traceln("| " + project.getName()); OrccLogger.traceln("+-------------------"); fsa.setOutputPath(OrccUtil.getOutputFolder(project) .getLocation().toString()); calGenerator.beforeBuild(project, resourceSet); for (final Resource res : resourcesMap.get(project)) { calGenerator.doGenerate(res, fsa); OrccLogger.traceln("Build " + res.getURI().toString()); } calGenerator.afterBuild(); } // Avoid warning messages of type "The workspace exited // with unsaved changes in the previous session" the next // time an IApplication (FrontendCli) will be launched // This method can be called ONLY if auto-building has // been disabled OrccLogger.traceln("Saving workspace"); workspace.getRoot().refreshLocal(IWorkspaceRoot.DEPTH_INFINITE, new NullProgressMonitor()); workspace.save(true, new NullProgressMonitor()); final IJobManager manager = Job.getJobManager(); int i = 0; while (!manager.isIdle()) { OrccLogger.traceln("Waiting for completion of" + " currently running jobs - " + ++i); Thread.sleep(500); } } catch (CoreException e) { OrccLogger.severeln(e.getMessage()); e.printStackTrace(); } catch (FileNotFoundException e) { OrccLogger.severeln(e.getMessage()); e.printStackTrace(); } catch (Exception e) { OrccLogger.severeln(e.getMessage()); e.printStackTrace(); } finally { try { if (wasAutoBuildEnabled) { CommandLineUtil.enableAutoBuild(workspace); wasAutoBuildEnabled = false; } OrccLogger.traceln("End of frontend execution"); return IApplication.EXIT_OK; } catch (CoreException e) { OrccLogger.severeln(e.getMessage()); e.printStackTrace(); } } return IApplication.EXIT_RESTART; } /** * Parse the command line and initialize the project to work with. If a * network qualified name is passed in cli arguments, initialize the * networkFile member. * * @param args * @return */ private boolean parseCommandLine(final String[] args) { if (args.length == 0) { OrccLogger.severeln("Unable to parse command line arguments"); OrccLogger.traceln(USAGE); return false; } OrccLogger.traceln("Command line arguments are \"" + StringUtils.join(args, ' ') + "\""); project = workspace.getRoot().getProject(args[0]); if (project == null) { OrccLogger.severeln("Unable to find the project " + args[0]); OrccLogger.traceln(USAGE); return false; } if (args.length >= 2 && !args[1].isEmpty()) { networkFile = OrccUtil.getFile(project, args[1], OrccUtil.NETWORK_SUFFIX); if (networkFile == null) { OrccLogger.severeln("Unable to find the network " + args[1]); } } return true; } /** * Return a Collection containing all projects required to build the given * project. The collection is sorted in the correct build order: the given * project will be the last in the resulting Collection. * * @param project * @return * @throws JavaModelException */ private Collection<IProject> getOrderedProjects(final IProject project) throws JavaModelException { final Collection<IProject> projects = new LinkedHashSet<IProject>(); final IJavaProject javaProject = JavaCore.create(project); if (javaProject == null) { OrccLogger.severeln("Project " + project.getName() + " is not a Java project."); return projects; } for (final String required : javaProject.getRequiredProjectNames()) { final IProject proj = OrccUtil.workspaceRoot().getProject(required); projects.addAll(getOrderedProjects(proj)); } projects.add(project); return projects; } /** * <p> * Returns the EMF resource corresponding to the given file. The file must * exists in the workspace. * </p> * * <p> * Returned resource is fully loaded (it should not contains proxy objects) * </p> * * @param file * @return A loaded EMF resource */ private Resource getResource(final IFile file) { if (!file.exists()) { OrccLogger.severeln("File " + file.getFullPath().toString() + " does not exists !"); return null; } final URI uri = URI.createPlatformResourceURI(file.getFullPath() .toString(), true); return resourceSet.getResource(uri, true); } /** * <p> * Parse the given <em>netFile</em> network file, and use the given * <em>workspaceMap</em> to build a list of actors it references. * </p> * * <p> * Fill the given <em>files</em> Multimap with all actors referenced in the * network (and its sub-networks) and all units imported in these actors. * Each Multimap entry is indexed by the project where the corresponding * IFile is stored. * </p> * * @param netFile * An IFile instance, containing a XDF network * @param workspaceMap * A map of all workspace files, indexed by their qualified name * @param files * The resulting Multimap, containing all files needed to build * the network, indexed by the project of each file. * @throws FileNotFoundException */ private void storeReferencedEntities(final IFile netFile, final Map<String, IFile> workspaceMap, final Multimap<IProject, Resource> files) throws FileNotFoundException { final Document document = DomUtil.parseDocument(new FileInputStream( netFile.getLocation().toFile())); final Element root = document.getDocumentElement(); final NodeList children = root.getChildNodes(); for (int i = 0; i < children.getLength(); ++i) { final Node child = children.item(i); if (child.getNodeType() != Node.ELEMENT_NODE) { // Only ELEMENT nodes in XDF file continue; } final Element tag = (Element) child; if (tag.getNodeName().equals(XdfConstants.INSTANCE_TAG)) { final NodeList instChildren = tag.getChildNodes(); for (int j = 0; j < instChildren.getLength(); ++j) { final Node instChild = instChildren.item(j); if (instChild.getNodeType() != Node.ELEMENT_NODE) continue; final Element classElement = (Element) instChild; if (classElement.getNodeName().equals( XdfConstants.CLASS_TAG)) { final String qualifiedName = classElement .getAttribute(XdfConstants.NAME_ATTR); if (workspaceMap.containsKey(qualifiedName)) { final IFile file = workspaceMap.get(qualifiedName); if (OrccUtil.NETWORK_SUFFIX.equals(file .getFileExtension())) { // Run this method on the sub-network storeReferencedEntities(file, workspaceMap, files); } else { // Store the imports of the CAL actor storeImportedResources(file, files); // Store the CAL Actor itself files.put(file.getProject(), getResource(file)); } } } } } } } /** * Stores in given <em>resultMap</em> all Units files imported by the given * <em>calFile</em>. * * @param calFile * @param resultMap */ private void storeImportedResources(final IFile calFile, final Multimap<IProject, Resource> resultMap) { final AstEntity astEntity = (AstEntity) getResource(calFile) .getContents().get(0); final EList<Import> imports = astEntity.getImports(); for (final Import imp : imports) { final String namespace = imp.getImportedNamespace(); final String qname = namespace.substring(0, namespace.lastIndexOf('.')); final IFile importedFile = OrccUtil.getFile(project, qname, OrccUtil.CAL_SUFFIX); // The imported file can import files itself storeImportedResources(importedFile, resultMap); resultMap.put(importedFile.getProject(), getResource(importedFile)); } } /** * Unused */ @Override public void stop() { } }