npanday.executable.impl.CompilerContextImpl.java Source code

Java tutorial

Introduction

Here is the source code for npanday.executable.impl.CompilerContextImpl.java

Source

package npanday.executable.impl;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import npanday.ArtifactType;
import npanday.ArtifactTypeHelper;
import npanday.PlatformUnsupportedException;
import npanday.RepositoryNotFoundException;
import npanday.executable.ExecutionException;
import npanday.executable.compiler.CompilerCapability;
import npanday.executable.compiler.CompilerConfig;
import npanday.executable.compiler.CompilerContext;
import npanday.executable.compiler.CompilerExecutable;
import npanday.executable.compiler.InvalidArtifactException;
import npanday.executable.compiler.KeyInfo;
import npanday.registry.Repository;
import npanday.registry.RepositoryRegistry;
import npanday.vendor.Vendor;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.model.Dependency;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.logging.LogEnabled;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.PathTool;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * Provides an implementation of the Compiler Context.
 *
 * @author Shane Isbell
 * @plexus.component role="npanday.executable.compiler.CompilerContext"
 */
public final class CompilerContextImpl extends ExecutableContextImpl implements CompilerContext, LogEnabled {

    private String NEW_LINE = System.getProperty("line.separator");

    /**
     * The maven project
     */
    private MavenProject project;

    private CompilerConfig config;

    private CompilerCapability compilerCapability;

    private List<Artifact> libraries;

    private List<Artifact> directLibraries;

    private List<Artifact> modules;

    /**
     * @plexus.requirement
     */
    private RepositoryRegistry repositoryRegistry;

    /**
     * A logger for writing log messages
     */
    private Logger logger;

    private List<File> linkedResources;

    /**
     * @deprecated
     */
    private List<File> embeddedResources;

    private List<String> embeddedResourceArgs;

    private File win32icon;

    private List<File> win32resources;

    public List<File> getLinkedResources() {
        return linkedResources;
    }

    @Deprecated
    public List<File> getEmbeddedResources() {
        return embeddedResources;
    }

    public List<String> getEmbeddedResourceArgs() {
        return embeddedResourceArgs;
    }

    public File getWin32Icon() {
        return win32icon;
    }

    public List<File> getWin32Resources() {
        return win32resources;
    }

    public File getAssemblyPath() {
        return Objects.firstNonNull(config.getAssemblyPath(), compilerCapability.getAssemblyPath());
    }

    public String getTargetFramework() {
        // TODO: Target framework could be overridden through the config here...
        return compilerCapability.getTargetFramework();
    }

    public String getTargetProfile() {
        return compilerCapability.getProfile();
    }

    public ArtifactType getTargetArtifactType() {
        return config.getArtifactType();
    }

    public String getFrameworkVersion() {
        return compilerCapability.getVendorInfo().getFrameworkVersion();
    }

    public boolean isTestCompile() {
        return config.isTestCompile();
    }

    public boolean shouldCompile() {
        if (isTestCompile() && getSourceFiles().size() == 0) {
            logger.info("NPANDAY-061-017: Skipping test compile; no sources found");
            return false;
        }

        return true;
    }

    public void enableLogging(Logger logger) {
        this.logger = logger;
    }

    public List<String> getCoreAssemblyNames() {
        return compilerCapability.getCoreAssemblies();
    }

    public List<Artifact> getModuleDependencies() {
        return modules;
    }

    public List<Artifact> getDirectModuleDependencies() {
        List<Artifact> artifacts = Lists.newArrayList();

        if (config.isTestCompile() && ArtifactTypeHelper.isDotnetModule(config.getArtifactType())) {
            artifacts.add(project.getArtifact());
        }

        if (config.isTestCompile() && ArtifactTypeHelper.isDotnetModule(project.getArtifact().getType())
                && project.getArtifact().getFile() != null && project.getArtifact().getFile().exists()) {
            artifacts.add(project.getArtifact());
        }
        return artifacts;
    }

    public KeyInfo getKeyInfo() {
        if ((compilerCapability.getVendorInfo().getVendor().equals(Vendor.MICROSOFT)
                && compilerCapability.getVendorInfo().getFrameworkVersion().equals("1.1.4322"))
                || config.getKeyInfo() == null) {
            return KeyInfo.Factory.createDefaultKeyInfo();
        } else {
            return config.getKeyInfo();
        }
    }

    public List<Artifact> getLibraryDependencies() {

        addProjectArtifactForTestCompile(libraries);
        return libraries;
    }

    private void addProjectArtifactForTestCompile(List<Artifact> libraries) {
        if (config.isTestCompile()
                && (ArtifactTypeHelper.isDotnetLibrary(config.getArtifactType())
                        || ArtifactTypeHelper.isDotnetMavenPlugin(config.getArtifactType()))
                && project.getArtifact().getFile() != null && project.getArtifact().getFile().exists()
                && !libraries.contains(project.getArtifact())
                && !ArtifactTypeHelper.isDotnetModule(project.getArtifact().getType())) {
            libraries.add(project.getArtifact());
        }
    }

    public List<Artifact> getDirectLibraryDependencies() {
        for (Iterator i = project.getDependencyArtifacts().iterator(); i.hasNext();) {
            Artifact artifact = (Artifact) i.next();

            if (!new ScopeArtifactFilter(isTestCompile() ? "test" : "compile").include(artifact)) {
                continue;
            }

            // TODO: use isAddedToClassPath instead? May need to annotate types
            if (!ArtifactTypeHelper.isDotnetLibrary(artifact.getType())
                    && !ArtifactTypeHelper.isDotnetExecutable(artifact.getType())
                    && !ArtifactTypeHelper.isDotnetAnyGac(artifact.getType())) {
                continue;
            }

            if (!hasArtifact(artifact)) {
                directLibraries.add(artifact);
            }

            boolean found = false;
            for (Iterator j = project.getDependencies().iterator(); j.hasNext() && !found;) {
                Dependency dependency = (Dependency) j.next();
                if (dependency.getGroupId().equals(artifact.getGroupId())
                        && dependency.getArtifactId().equals(artifact.getArtifactId())
                        && dependency.getVersion().equals(artifact.getBaseVersion())) {
                    found = true;
                }
            }

            if (!found) {
                directLibraries.remove(artifact);
            }

        }

        addProjectArtifactForTestCompile(directLibraries);

        return directLibraries;

    }

    private boolean hasArtifact(Artifact artifact) {
        for (Artifact art : directLibraries) {
            if (art.getArtifactId().equals(artifact.getArtifactId())) {
                return true;
            }
        }
        return false;
    }

    public Logger getLogger() {
        return logger;
    }

    public File getGeneratedSourcesDirectory() {
        return (config.isTestCompile()) ? new File(project.getBuild().getDirectory(), "build-test-sources")
                : new File(project.getBuild().getDirectory(), "build-sources");
    }

    public File getTargetDirectory() {
        return new File(project.getBuild().getDirectory());
    }

    /**
     * This method will return a File where File.isExist() returns false, if NetCompile.compile has not been
     * invoked.
     *
     * @return
     * @throws InvalidArtifactException
     */
    public File getArtifact() throws InvalidArtifactException {
        ArtifactType artifactType = config.getArtifactType();
        if (artifactType == null || artifactType.equals(ArtifactType.NULL)) {
            throw new InvalidArtifactException("NPANDAY-061-001: Artifact Type cannot be null");
        }

        //TODO: The test-plugin has a dependency on this fileName/dir. If we change it here,
        // it will break the plugin. Fix this encapsulation issue.
        String fileName = (config.isTestCompile())
                ? project.getBuild().getDirectory() + File.separator + project.getArtifactId() + "-test.dll"
                : project.getBuild().getDirectory() + File.separator + project.getArtifactId() + "."
                        + artifactType.getExtension();
        return new File(fileName);
    }

    public CompilerExecutable getCompilerExecutable() throws ExecutionException {
        return (CompilerExecutable) getNetExecutable();
    }

    public Repository find(String repositoryName) throws RepositoryNotFoundException {
        Repository repository = repositoryRegistry.find(repositoryName);
        if (repository == null) {
            throw new RepositoryNotFoundException(
                    "NPANDAY-061-002: Could not find repository: Name = " + repositoryName);
        }
        return repository;
    }

    public void init(CompilerCapability capability, CompilerConfig config, MavenProject project)
            throws PlatformUnsupportedException {

        this.project = project;
        this.config = config;
        libraries = new ArrayList<Artifact>();
        directLibraries = new ArrayList<Artifact>();
        modules = new ArrayList<Artifact>();
        compilerCapability = capability;

        // initialize base class
        super.init(compilerCapability, config);

        Set<Artifact> artifacts = project.getDependencyArtifacts();//Can add WFC deps prior
        if (artifacts != null) {
            for (Artifact artifact : artifacts) {
                String type = artifact.getType();
                logger.debug("NPANDAY-061-006: Artifact Type:" + type);
                logger.debug("NPANDAY-061-007: Artifact Type:" + ArtifactTypeHelper.isDotnetGenericGac(type));
                ArtifactType artifactType = ArtifactType.getArtifactTypeForPackagingName(type);
                if (ArtifactTypeHelper.isDotnetModule(type)) {
                    modules.add(artifact);
                } else if (ArtifactTypeHelper.isDotnetAssembly(artifactType)) {
                    libraries.add(artifact);
                }

                if (type.equals(ArtifactType.COM_REFERENCE.getPackagingType())) {
                    moveInteropDllToBuildDirectory(artifact);
                }
            }
        }

        String basedir = project.getBuild().getDirectory() + File.separator + "assembly-resources" + File.separator;
        linkedResources = new File(basedir, "linkresource").exists()
                ? Arrays.asList(new File(basedir, "linkresource").listFiles())
                : new ArrayList<File>();
        getEmbeddedResources(new File(basedir, "resource"));
        win32resources = new File(basedir, "win32res").exists()
                ? Arrays.asList(new File(basedir, "win32res").listFiles())
                : new ArrayList<File>();
        File win32IconDir = new File(basedir, "win32icon");
        if (win32IconDir.exists()) {
            File[] icons = win32IconDir.listFiles();
            if (icons.length > 1) {
                throw new PlatformUnsupportedException(
                        "NPANDAY-061-008: There is more than one win32icon in resource directory: Number = "
                                + icons.length);
            }
            if (icons.length == 1) {
                win32icon = icons[0];
            }
        }
    }

    Set<File> expandedSourceFiles;

    public Set<File> getSourceFiles() {
        // TODO: cache source files - but it seems that this instance is reused... ??
        /*if (expandedSourceFiles == null)
        expandedSourceFiles = expandSourceFiles();*/

        return expandSourceFiles();
    }

    private Set<File> expandSourceFiles() {
        Set<File> files = Sets.newHashSet();

        files.addAll(expandSources(getGeneratedSourcesDirectory()));

        String defaultSourceRoot = isTestCompile() ? project.getBuild().getTestSourceDirectory()
                : project.getBuild().getSourceDirectory();

        Set<String> additionalRoots = Sets.newHashSet();
        List bareRoots = isTestCompile() ? project.getTestCompileSourceRoots() : project.getCompileSourceRoots();

        if (bareRoots != null) {
            for (Object root : bareRoots) {
                if (!root.equals(defaultSourceRoot)) {
                    additionalRoots.add((String) root);
                }
            }
        }

        if (additionalRoots.size() > 0) {
            getLogger().debug("NPANDAY-061-009: Adding additional compile source roots: " + additionalRoots);

            for (String root : additionalRoots) {
                files.addAll(expandSources(new File(root)));
            }
        }

        if (isTestCompile()) {
            files.addAll(expandTestSourceFilePatterns());
        } else if (!isSourceAndTestsTogether()) {
            files.addAll(expandMainSourceFilePatterns());
        } else {
            List<File> mainSources = expandMainSourceFilePatterns();
            List<File> testSources = expandTestSourceFilePatterns();

            getLogger().debug("NPANDAY-061-014: Since tests (" + testSources.size()
                    + " files) reside in same folder as main sources (" + mainSources.size() + " files),"
                    + " they will be excluded from main sources");

            List<File> sources = Lists.newArrayList();
            sources.addAll(mainSources);
            sources.removeAll(testSources);
            files.addAll(sources);
        }

        logger.info("NPANDAY-061-011: Found " + files.size() + " source files for "
                + (isTestCompile() ? "test" : "main") + " compile");

        return files;
    }

    private List<File> expandMainSourceFilePatterns() {
        getLogger().debug("NPANDAY-061-012: Expanding main sources");
        List<String> includes = Lists.newArrayList();
        if (config.getDeprecatedIncludeSourcesConfiguration() != null) {
            includes.addAll(Lists.newArrayList(config.getDeprecatedIncludeSourcesConfiguration()));
        }
        if (config.getIncludes() != null) {
            includes.addAll(Lists.newArrayList(config.getIncludes()));
        }
        List<String> excludes = Lists.newArrayList();
        if (config.getExcludes() != null) {
            excludes.addAll(Lists.newArrayList(config.getExcludes()));
        }

        if (includes.size() == 0) {
            includes.add("**/*." + config.getLanguageFileExtension());
        }

        //target files
        excludes.add("**/obj/**");
        excludes.add("**/bin/**");
        excludes.add("**/target/**");

        File root = new File(project.getBuild().getSourceDirectory());

        return expandSources(root, includes, excludes);
    }

    private List<File> expandTestSourceFilePatterns() {
        getLogger().debug("NPANDAY-061-013: Expanding test sources");
        List<String> includes = Lists.newArrayList();
        if (config.getDeprecatedIncludeTestSourcesConfiguration() != null) {
            includes.addAll(Lists.newArrayList(config.getDeprecatedIncludeTestSourcesConfiguration()));
        }
        if (config.getTestIncludes() != null) {
            includes.addAll(Lists.newArrayList(config.getTestIncludes()));
        }
        List<String> excludes = Lists.newArrayList();
        if (config.getTestExcludes() != null) {
            excludes.addAll(Lists.newArrayList(config.getTestExcludes()));
        }

        if (includes.size() == 0) {
            if (!isSourceAndTestsTogether()) {
                includes.add("**/*." + config.getLanguageFileExtension());
            } else {
                getLogger().debug("NPANDAY-061-014: Since source and tests reside in same folder, "
                        + "and no default includes are stated, conventions for finding tests will be applied.");
                includes.add("**/Test/**/*.*" + config.getLanguageFileExtension());
                includes.add("**/Tests/**/*.*" + config.getLanguageFileExtension());
                includes.add("**/*Tests." + config.getLanguageFileExtension());
                includes.add("**/*Test." + config.getLanguageFileExtension());
            }
        }

        //target files
        excludes.add("**/obj/**");
        excludes.add("**/bin/**");
        excludes.add("**/target/**");

        File root = new File(project.getBuild().getTestSourceDirectory());

        return expandSources(root, includes, excludes);
    }

    private List<File> expandSources(File directory) {
        return expandSources(directory, Lists.newArrayList("**/*." + config.getLanguageFileExtension()), null);
    }

    private List<File> expandSources(File directory, Iterable<String> includes, Iterable<String> excludes) {
        if (!directory.exists() || directory.list().length == 0) {
            getLogger().debug("NPANDAY-061-015: " + directory + " is empty; no sources found");
            return Lists.newArrayList();
        }

        DirectoryScanner scanner = createScanner(directory, includes, excludes);
        scanner.scan();

        List<File> files = Lists.newArrayList();
        for (String fs : scanner.getIncludedFiles()) {

            if (!fs.endsWith("." + config.getLanguageFileExtension())) {
                continue;
            }

            files.add(new File(directory, fs));
        }

        if (getLogger().isDebugEnabled()) {
            getLogger().debug("NPANDAY-061-016: scanned for source files:" + NEW_LINE + " - directory: "
                    + directory.getAbsolutePath() + NEW_LINE + " - includes: " + includes + NEW_LINE
                    + " - excludes: " + excludes + NEW_LINE + " - included (*.*): "
                    + scanner.getIncludedFiles().length + NEW_LINE + " - included sources (*."
                    + config.getLanguageFileExtension() + "): " + files.size() + NEW_LINE + " - excluded: "
                    + scanner.getExcludedFiles().length + NEW_LINE + " - ignored: "
                    + scanner.getNotIncludedFiles().length);
        }

        return files;
    }

    private DirectoryScanner createScanner(File root, Iterable<String> includes, Iterable<String> excludes) {
        DirectoryScanner scanner = new DirectoryScanner();
        scanner.setBasedir(root);

        if (includes != null) {
            scanner.setIncludes(Iterables.toArray(includes, String.class));
        }
        if (excludes != null) {
            scanner.setExcludes(Iterables.toArray(excludes, String.class));
        }

        // TODO: NPANDAY-210 Maven is usually case sensitive, right?
        scanner.setCaseSensitive(false);
        scanner.addDefaultExcludes();
        return scanner;
    }

    public File getOutputDirectory() {
        return config.getOutputDirectory();
    }

    private void getEmbeddedResources(File basedir) {
        List<File> embeddedResources = new ArrayList<File>();
        List<String> embeddedResourceArgs = new ArrayList<String>();
        if (basedir.exists()) {
            DirectoryScanner scanner = new DirectoryScanner();
            scanner.setBasedir(basedir);
            scanner.scan();

            for (String file : scanner.getIncludedFiles()) {
                File f = new File(basedir, file);
                embeddedResources.add(f);

                String executionRoot = System.getProperty("user.dir");
                // TODO: ideally, all execution would happen from the project basedir, not the user.dir
                //String executionRoot = project.getBasedir().getAbsolutePath();
                String path = PathTool.getRelativeFilePath(executionRoot, f.getPath());

                if (f.getName().endsWith(".resources")) {
                    embeddedResourceArgs.add(path);
                } else {
                    String resourceName = project.getArtifactId() + "." + file.replace(File.separatorChar, '.');
                    embeddedResourceArgs.add(path + "," + resourceName);
                }
            }
        }
        this.embeddedResources = embeddedResources;
        this.embeddedResourceArgs = embeddedResourceArgs;
    }

    private void moveInteropDllToBuildDirectory(Artifact artifact) throws PlatformUnsupportedException {
        try {
            File file = artifact.getFile();
            String oldPath = file.getAbsolutePath();
            String target = project.getBuild().getDirectory();
            String newPath = target + File.separator + "Interop." + artifact.getArtifactId() + ".dll";

            if (oldPath.contains(target)) //already copied to target
            {
                return;
            }

            logger.info("NPANDAY-000-000:[COM Reference] copying file [" + oldPath + "] to [" + target + "]");
            FileUtils.copyFileToDirectory(file, new File(target));

            logger.info("NPANDAY-000-000:[COM Reference] deleting directory [" + file.getParentFile() + "]");
            FileUtils.deleteDirectory(file.getParentFile());

            logger.info("NPANDAY-000-000:[COM Reference] updating artifact path to [" + newPath + "]");

            artifact.setFile(new File(newPath));
        } catch (Exception e) {
            throw new PlatformUnsupportedException(e);
        }
    }

    public boolean isSourceAndTestsTogether() {
        return project.getBuild().getSourceDirectory().equals(project.getBuild().getTestSourceDirectory());
    }
}