com.redhat.ceylon.ant.CeylonCompileAntTask Maven / Gradle / Ivy
/* Originally based on the javac task from apache-ant-1.7.1.
* The license in that file is as follows:
*
* 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.
*
*/
/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*/
package com.redhat.ceylon.ant;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import com.redhat.ceylon.common.Constants;
@ToolEquivalent("compile")
@AntDoc("To compile the module `com.example.foo` whose source code is in the\n"+
"`src` directory to a module repository in the `build` directory, with\n"+
"verbose compiler messages:\n"+
"\n"+
"\n"+
" \n"+
" \n"+
" \n"+
" \n"+
" \n")
public class CeylonCompileAntTask extends LazyCeylonAntTask {
static final String FAIL_MSG = "Compile failed; see the compiler error output for details.";
@AntDoc("For example:\n\n"
+ "\n"
+ " UTF-8 \n\n"
+ "or\n\n"
+ "\n"
+ " >\n")
public static class JavacOption {
String key;
String value;
@AntDoc("The name of the `javac` option")
@Required
public void setKey(String key) {
this.key = key;
}
@AntDoc("The value of the `javac` option. "
+ "Required when the corresponding `javac` option requires an argument")
public void setValue(String value) {
this.value = value;
}
public void addText(String value) {
this.value = value;
}
}
@AntDoc("Suppresses compiler warnings. Warnings can be suppressed by "
+ "type, for example\n"
+ "\n"
+ "\n"
+ " filenameNonAscii \n"
+ "\n"
+ "or eqiuvalently \n"
+ "\n"
+ "\n"
+ " \n"
+ "\n"
+ "or all warnings can be suppressed by not naming any "
+ "specific warnings:\n"
+ "\n"
+ "\n"
+ " \n"
+ "\n")
public static class SuppressWarning {
String value;
@AntDoc("The name of the warning(s) to be suppressed.")
public void setValue(String value) {
this.value = value;
}
@AntDocIgnore
public void addText(String value) {
this.value = value;
}
}
private static final FileFilter ARTIFACT_FILTER = new FileFilter() {
@Override
public boolean accept(File pathname) {
String name = pathname.getName();
return name.endsWith(".car")
|| name.endsWith(".src")
|| name.endsWith(".car.sha1")
|| name.endsWith(".src.sha1");
}
};
private Path res;
private Path classpath;
private final ModuleSet moduleSet = new ModuleSet();
private FileSet files;
private List javacOptions = new ArrayList(0);
private Boolean noOsgi;
private String osgiProvidedBundles;
private Boolean jigsaw;
private Boolean noPom;
private Boolean pack200;
private List suppressWarnings = new ArrayList(0);
private boolean suppressAllWarnings = false;
private List compileList = new ArrayList(2);
private Set modules = null;
public CeylonCompileAntTask() {
super("compile");
}
/**
* Set to true to disable OSGi manifest declaration in the META-INF/MANIFEST.MF car file.
*/
@OptionEquivalent
public void setNoOsgi(Boolean noOsgi) {
this.noOsgi = noOsgi;
}
public boolean getNoOsgi() {
return noOsgi;
}
/**
* Set to true to enable Java 9 (Jigsaw) module-info.class generation in the car file.
*/
@OptionEquivalent
public void setGenerateModuleInfo(Boolean jigsaw) {
this.jigsaw = jigsaw;
}
public boolean getGenerateModuleInfo() {
return jigsaw;
}
/**
* Comma-separated list of module names.
* The listed modules are expected to be OSGI bundles provided by the framework,
* and will be omitted from the 'Required-Bundle' OSGI header in the
* manifest of the generated car file.
*/
@OptionEquivalent
public void setOsgiProvidedBundles(String osgiProvidedBundles) {
this.osgiProvidedBundles = osgiProvidedBundles;
}
public String getOsgiProvidedBundles() {
return osgiProvidedBundles;
}
/**
* Set to true to disable Maven POM module declaration in the META-INF/maven/ car folder.
*/
@OptionEquivalent
public void setNoPom(Boolean noPom) {
this.noPom = noPom;
}
public boolean getNoPom() {
return noPom;
}
public Boolean getPack200() {
return pack200;
}
/**
* Set to true to enable repacking the generated car file using pack200.
*/
@OptionEquivalent("--pack200")
public void setPack200(Boolean pack200) {
this.pack200 = pack200;
}
@OptionEquivalent
public void addConfiguredSuppressWarning(SuppressWarning sw) {
this.suppressWarnings.add(sw);
if (sw.value == null || sw.value.isEmpty()) {
suppressAllWarnings = true;
}
}
/**
* Set the resource directories to find the resource files.
* @param res the resource directories as a path
*/
@OptionEquivalent("--resource")
public void setResource(Path res) {
if (this.res == null) {
this.res = res;
} else {
this.res.append(res);
}
}
@OptionEquivalent("--resource")
public void addConfiguredResource(Src res) {
Path p = new Path(getProject(), res.value);
if (this.res == null) {
this.res = p;
} else {
this.res.append(p);
}
}
public List getResource() {
if (this.res == null) {
return Collections.singletonList(getProject().resolveFile(Constants.DEFAULT_RESOURCE_DIR));
}
String[] paths = this.res.list();
ArrayList result = new ArrayList(paths.length);
for (String path : paths) {
result.add(getProject().resolveFile(path));
}
return result;
}
/**
* Sets the classpath
* @param path
*/
@AntDocIgnore
public void setClasspath(Path path){
if(this.classpath == null)
this.classpath = path;
else
this.classpath.add(path);
}
/**
* Adds a <classpath> nested param
*/
public Path createClasspath(){
if(this.classpath == null)
return this.classpath = new Path(getProject());
else
return this.classpath.createPath();
}
/**
* Sets the classpath by a path reference
* @param classpathReference
*/
@AntDocIgnore
public void setClasspathref(Reference classpathReference) {
createClasspath().setRefid(classpathReference);
}
@AntDoc("Modules to be compiled")
public void addConfiguredModuleSet(ModuleSet moduleset) {
this.moduleSet.addConfiguredModuleSet(moduleset);
}
/**
* Adds a module to compile
* @param module the module name to compile
*/
@AntDoc("A module to be compiled")
public void addConfiguredModule(Module module) {
this.moduleSet.addConfiguredModule(module);
}
@AntDoc("Modules to be compiled")
public void addConfiguredSourceModules(SourceModules sourceModules) {
this.moduleSet.addConfiguredSourceModules(sourceModules);
}
@AntDoc("A `` containing Ceylon and Java files to be compiled (incremental compilation)")
public void addFiles(FileSet fileset) {
if (this.files != null) {
throw new BuildException(" only supports a single element");
}
this.files = fileset;
}
/** Adds an option to be passed to javac via a {@code --javac=...} option */
@OptionEquivalent("--javac")
public void addConfiguredJavacOption(JavacOption javacOption) {
this.javacOptions.add(javacOption);
}
/**
* Clear the list of files to be compiled and copied..
*/
protected void resetFileLists() {
compileList.clear();
}
/**
* Check that all required attributes have been set and nothing silly has
* been entered.
*
* @exception BuildException if an error occurs
*/
protected void checkParameters() throws BuildException {
if (this.moduleSet.getModules().isEmpty()
&& this.files == null) {
throw new BuildException("You must specify a , and/or ");
}
}
@Override
protected Commandline buildCommandline() {
resetFileLists();
if (files != null) {
addToCompileList(getSrc());
addToCompileList(getResource());
}
modules = moduleSet.getModules();
if (compileList.size() == 0 && modules.size() == 0){
log("Nothing to compile");
return null;
}
LazyHelper lazyTask = new LazyHelper(this) {
@Override
protected File getArtifactDir(Module module) {
File outModuleDir = new File(getOut(), module.toVersionedDir().getPath());
return outModuleDir;
}
@Override
protected FileFilter getArtifactFilter() {
return ARTIFACT_FILTER;
}
@Override
protected long getOldestArtifactTime(File file) {
long mtime = Long.MAX_VALUE;
String name = file.getPath().toLowerCase();
if (name.endsWith(".car") || name.endsWith(".src")) {
JarFile jarFile = null;
try {
jarFile = new JarFile(file);
Enumeration entries = jarFile.entries();
while(entries.hasMoreElements()){
JarEntry entry = entries.nextElement();
if (entry.getTime() < mtime) {
mtime = entry.getTime();
}
}
} catch (Exception ex) {
// Maybe something's wrong with the CAR so let's return MIN_VALUE
mtime = Long.MIN_VALUE;
} finally {
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
// Ignore
}
}
}
} else {
mtime = file.lastModified();
}
return mtime;
}
@Override
protected long getArtifactFileTime(Module module, File file) {
File moduleDir = getArtifactDir(module);
String name = module.getName() + ((module.getVersion() != null) ? "-" + module.getVersion() : "") ;
File carFile = new File(moduleDir, name + ".car");
File srcFile = new File(moduleDir, name + ".src");
long carTime = getCarEntryTime(carFile, file);
long srcTime = getZipEntryTime(srcFile, file);
return Math.min(carTime, srcTime);
}
private long getCarEntryTime(File carFile, File entryFile) {
long mtime = Long.MAX_VALUE;
String name = entryFile.getPath().replace('\\', '/');
Properties mapping = readMappingFromCar(carFile);
if (mapping != null) {
JarFile jarFile = null;
try {
jarFile = new JarFile(carFile);
for (String className : mapping.stringPropertyNames()) {
String srcName = mapping.getProperty(className);
if (name.equals(srcName) || name.endsWith("/" + srcName)) {
ZipEntry entry = jarFile.getEntry(className);
if (entry != null) {
mtime = Math.min(mtime, entry.getTime());
}
}
}
} catch (Exception ex) {
// Ignore
} finally {
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
// Ignore
}
}
}
}
return mtime;
}
private long getZipEntryTime(File zipFile, File entryFile) {
if (zipFile.isFile()) {
String name = entryFile.getPath().replace('\\', '/');
JarFile jarFile = null;
try {
jarFile = new JarFile(zipFile);
Enumeration entries = jarFile.entries();
while(entries.hasMoreElements()){
JarEntry entry = entries.nextElement();
if (name.equals(entry.getName()) || name.endsWith("/" + entry.getName())) {
return entry.getTime();
}
}
} catch (Exception ex) {
// Ignore
} finally {
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
// Ignore
}
}
}
}
return Long.MAX_VALUE;
}
private Properties readMappingFromCar(File carFile) {
if (carFile.isFile()) {
JarFile jarFile = null;
try {
jarFile = new JarFile(carFile);
ZipEntry entry = jarFile.getEntry("META-INF/mapping.txt");
if (entry != null) {
InputStream inputStream = jarFile.getInputStream(entry);
try {
Properties mapping = new Properties();
mapping.load(inputStream);
return mapping;
} finally {
inputStream.close();
}
}
} catch (Exception ex) {
// Ignore
} finally {
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
// Ignore
}
}
}
}
return null;
}
};
if (lazyTask.filterFiles(compileList)
&& lazyTask.filterModules(modules)) {
log("Everything's up to date");
return null;
}
return super.buildCommandline();
}
private void addToCompileList(List dirs) {
for (File srcDir : dirs) {
if (srcDir.isDirectory()) {
FileSet fs = (FileSet)this.files.clone();
fs.setDir(srcDir);
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
String[] files = ds.getIncludedFiles();
for(String fileName : files)
compileList.add(new File(srcDir, fileName));
}
}
}
/**
* Perform the compilation.
*/
@Override
protected void completeCommandline(Commandline cmd) {
super.completeCommandline(cmd);
for (JavacOption opt : javacOptions) {
String arg = (opt.key != null) ? opt.key + ":" + opt.value : opt.value;
appendOptionArgument(cmd, "--javac", arg);
}
for (File res : getResource()) {
appendOptionArgument(cmd, "--resource", res.getAbsolutePath());
}
if (noOsgi != null && noOsgi.booleanValue())
appendOption(cmd, "--no-osgi");
if (jigsaw != null && jigsaw.booleanValue())
appendOption(cmd, "--generate-module-info");
if (osgiProvidedBundles != null && !osgiProvidedBundles.isEmpty())
appendOptionArgument(cmd, "--osgi-provided-bundles", osgiProvidedBundles);
if (noPom != null && noPom.booleanValue())
appendOption(cmd, "--no-pom");
if (pack200!= null && pack200.booleanValue())
appendOption(cmd, "--pack200");
if (suppressWarnings != null) {
if (suppressAllWarnings) {
appendOption(cmd, "--suppress-warning");
} else {
for (SuppressWarning sw : suppressWarnings) {
appendOption(cmd, "--suppress-warning=" + sw.value);
}
}
}
if(classpath != null){
throw new RuntimeException("-classpath not longer supported");
/*String path = classpath.toString();
cmd.createArgument().setValue("-classpath");
cmd.createArgument().setValue(Util.quoteParameter(path));*/
}
// files to compile
for (File file : compileList) {
log("Adding source file: "+file.getAbsolutePath(), Project.MSG_VERBOSE);
cmd.createArgument().setValue(file.getAbsolutePath());
}
// modules to compile
for (Module module : modules) {
log("Adding module: "+module, Project.MSG_VERBOSE);
cmd.createArgument().setValue(module.toVersionlessSpec());
}
}
@Override
protected String getFailMessage() {
return FAIL_MSG;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy