/*
* Copyright 2015 - 2016 i-net software
*
* 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.inet.gradle.setup.abstracts;
import groovy.lang.Closure;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Date;
import java.util.Set;
import javax.inject.Inject;
import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.CopySpec;
import org.gradle.api.file.FileTree;
import org.gradle.api.internal.artifacts.publish.DefaultPublishArtifact;
import org.gradle.api.internal.file.CopyActionProcessingStreamAction;
import org.gradle.api.internal.file.FileLookup;
import org.gradle.api.internal.file.FileResolver;
import org.gradle.api.internal.file.copy.CopyAction;
import org.gradle.api.internal.file.copy.CopyActionExecuter;
import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
import org.gradle.api.internal.file.copy.CopySpecInternal;
import org.gradle.api.internal.file.copy.CopySpecResolver;
import org.gradle.api.internal.file.copy.DefaultCopySpec;
import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
import org.gradle.api.internal.project.ProjectInternal;
import org.gradle.api.internal.tasks.SimpleWorkResult;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.WorkResult;
import org.gradle.internal.nativeplatform.filesystem.FileSystem;
import org.gradle.internal.reflect.Instantiator;
/**
* Base class for all setup task.
*
* @author Volker Berlin
*/
public abstract class AbstractTask extends DefaultTask implements SetupSources {
private final CopySpecInternal rootSpec;
private AbstractSetupBuilder setupBuilder;
private String extension, classifier;
/**
* Constructor with indication to artifact result
* Runs with the default SetupBuilder for dmg, msi ...
* @param extension of the setup
* @param setupType the class of the SetupBuilder
*/
@SuppressWarnings("unchecked")
public AbstractTask( String extension, Class<? extends AbstractSetupBuilder> setupType ) {
this.extension = extension;
this.rootSpec = (CopySpecInternal)getProject().copySpec( (Closure<CopySpec>)null );
ProjectInternal project = (ProjectInternal)getProject();
setupBuilder = project.getExtensions().getByType( setupType );
setGroup( "build" ); // for displaying in buildship
}
/**
* The action called from Gradle
*/
@TaskAction
public void action() {
build();
File setupFile = getSetupFile();
if( !setupFile.exists() ) {
throw new GradleException( "Setup file was not created: " + setupFile );
}
Configuration archives = getProject().getConfigurations().getByName( "archives" );
archives.getArtifacts().add( new DefaultPublishArtifact( setupBuilder.getAppIdentifier(), extension, extension, classifier, new Date(setupFile.lastModified()), setupFile, this ) );
}
/**
* Copy all files of this task to the given target.
* @param target the target directory
*/
public void copyTo( File target ) {
processFiles( new CopyActionProcessingStreamAction() {
@Override
public void processFile( FileCopyDetailsInternal details ) {
// details.copyTo( details.getRelativePath().getFile( target ) ); // didn't work with mounted smb devises under Unix
if( !details.isDirectory() ) {
try {
File f = details.getRelativePath().getFile( target );
if(!f.getParentFile().exists()) {
f.getParentFile().mkdirs(); // the parent directory must be created, else the copy fails
}
try( InputStream input = details.open() ) {
Files.copy( input, f.toPath(), StandardCopyOption.REPLACE_EXISTING );
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
} );
}
/**
* Handle all files of this task.
* @param action the action that should be process for every file
*/
protected void processFiles( CopyActionProcessingStreamAction action ) {
processFiles( action, setupBuilder.getRootSpec() );
processFiles( action, rootSpec );
}
/**
* Handle all files of the CopySpec.
* @param action the action that should be process for every file
*/
private void processFiles( CopyActionProcessingStreamAction action, CopySpecInternal copySpec ) {
if( setupBuilder.isFailOnEmptyFrom() ) {
for( CopySpecInternal cs : copySpec.getChildren() ) {
CopySpecResolver rootResolver = cs.buildRootResolver();
Set<File> files = rootResolver.getAllSource().getFiles();
if( files.size() == 0 ) {
throw new IllegalArgumentException( "No files selected by: " + ((DefaultCopySpec)cs).getSourcePaths() + " --> " + rootResolver.getDestPath() + ". This means that there are files missing or your 'from' method in your gradle script is wrong. If an empty 'from' is valid then disable the check with 'setupBuilder.failOnEmptyFrom = false'" );
}
int includeCount = cs.getIncludes().size();
if( files.size() < includeCount ) {
StringBuilder msg = new StringBuilder( "Not every 'include' match a file by: " );
msg.append( ((DefaultCopySpec)cs).getSourcePaths() );
msg.append( "\n\tDeclared includes:");
for( String include : cs.getIncludes() ) {
msg.append( "\n\t\t" ).append( include );
}
msg.append( "\n\tMatching files:" );
for( File file : files ) {
msg.append( "\n\t\t" ).append( file );
}
throw new IllegalArgumentException( msg.toString() );
}
}
}
CopyActionExecuter copyActionExecuter = new CopyActionExecuter( getInstantiator(), getFileSystem() );
CopyAction copyAction = new CopyAction() {
@Override
public WorkResult execute( CopyActionProcessingStream stream ) {
stream.process( action );
return new SimpleWorkResult( true );
}
};
copyActionExecuter.execute( copySpec, copyAction );
}
@Inject
protected Instantiator getInstantiator() {
throw new UnsupportedOperationException();
}
@Inject
protected FileSystem getFileSystem() {
throw new UnsupportedOperationException();
}
@Inject
protected FileResolver getFileResolver() {
throw new UnsupportedOperationException();
}
@Inject
protected FileLookup getFileLookup() {
throw new UnsupportedOperationException();
}
/**
* The platform depending build.
*/
public abstract void build();
/**
* Return the setupBuilder using the specified type
* @return setupBuilder
*/
@SuppressWarnings("unchecked")
protected AbstractSetupBuilder getAbstractSetupBuilder() {
return setupBuilder;
}
@Override
public CopySpecInternal getRootSpec() {
return rootSpec;
}
/**
* Overridden for annotation. {@inheritDoc}
*/
@InputFiles
@Override
public FileTree getSource() {
return SetupSources.super.getSource();
}
/**
* The setup Sources
* @return FileTree
*/
@InputFiles
public FileTree getSetupSource() {
try {
return setupBuilder.getSource();
} catch ( Throwable e ) {
throw new IllegalArgumentException( "You have to specify input sources for your application", e );
}
}
/**
* The resulting application
* @return the application
*/
@OutputFile
public File getSetupFile() {
StringBuilder setupFile = new StringBuilder(setupBuilder.getArchiveName());
if( getClassifier() != null && !getClassifier().isEmpty() ) {
setupFile.append( '-' );
setupFile.append( getClassifier() );
}
setupFile.append( '.' );
setupFile.append( getExtension() );
return new File( setupBuilder.getDestinationDir(), setupFile.toString() );
}
/**
* Get the file extension.
*
* @return the extension
*/
public String getExtension() {
return extension;
}
/**
* Set the file extension of the installer. The default is equals the task name.
*
* @param extension the file extension
*/
public void setExtension( String extension ) {
this.extension = extension;
}
/**
* Returns the classifier part of the installer, if any.
*
* @return The classifier. May be null.
*/
public String getClassifier() {
return classifier;
}
/**
* Set the classifier part of the installer.
*
* @param classifier The classifier. May be null.
*/
public void setClassifier(String classifier) {
this.classifier = classifier;
}
/**
* {@inheritDoc}
*/
@Override
public String getDescription() {
String desc = super.getDescription();
if( desc != null && !desc.isEmpty() ) {
return desc;
}
return setupBuilder.getDescription();
}
}