blob: fb2a0d4940fc0159a0a4fc28b28789bd35ccfdd4 [file] [log] [blame]
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() );
}
}