com.redhat.ceylon.tools.new_.CeylonNewTool Maven / Gradle / Ivy
/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package com.redhat.ceylon.tools.new_;
import java.io.File;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.redhat.ceylon.common.Constants;
import com.redhat.ceylon.common.Versions;
import com.redhat.ceylon.common.config.CeylonConfig;
import com.redhat.ceylon.common.config.DefaultToolOptions;
import com.redhat.ceylon.common.tool.CeylonBaseTool;
import com.redhat.ceylon.common.tool.Description;
import com.redhat.ceylon.common.tool.Hidden;
import com.redhat.ceylon.common.tool.OptionArgument;
import com.redhat.ceylon.common.tool.Rest;
import com.redhat.ceylon.common.tool.Subtool;
import com.redhat.ceylon.common.tool.Summary;
import com.redhat.ceylon.common.tool.ToolModel;
import com.redhat.ceylon.common.tools.CeylonTool;
@Summary("Generates a new Ceylon project")
@Description("Generates a new project, prompting for information as necessary")
public class CeylonNewTool extends CeylonBaseTool {
private ToolModel model;
private Project project;
private File from = new File(System.getProperty(Constants.PROP_CEYLON_HOME_DIR), "templates");
private Map rest = new HashMap();
public void setToolModel(ToolModel model) {
this.model = model;
}
@Subtool(argumentName="name", classes={Simple.class, HelloWorld.class, JavaInterop.class}, order=2)
public void setProject(Project project) {
this.project = project;
}
@Hidden
@OptionArgument(argumentName="dir")
public void setFrom(File from) {
this.from = from;
}
@Rest
public void setRest(List rest) {
for (String optionArg : rest) {
if (optionArg.startsWith("--")) {
int idx = optionArg.indexOf("=");
String option;
String arg;
if (idx == -1) {
option = optionArg.substring(2);
arg = "";
} else {
option = optionArg.substring(2, idx);
arg = optionArg.substring(idx + 1);
}
this.rest.put(option, arg);
}
}
}
@Override
public void initialize(CeylonTool mainTool) {
}
@Override
public void run() throws Exception {
File fromDir = getFromDir();
if (!fromDir.exists() || !fromDir.isDirectory()) {
throw new IllegalArgumentException(Messages.msg("from.nonexistent.or.nondir", fromDir));
}
Environment env = buildPromptedEnv();
List resources = project.getResources(env);
// Create base dir only once all the prompting has been done
project.mkBaseDir(getCwd());
for (Copy copy : resources) {
copy.run(env);
}
}
private String getProjectName() {
String projectName = model.getSubtoolModel().getToolLoader().getToolName(project.getClass().getName());
return projectName;
}
private File getFromDir() {
return new File(applyCwd(from), getProjectName());
}
private Environment buildPromptedEnv() {
Environment env = new Environment();
// TODO Tidy up how we create and what's in this initial environment
if (project.getDirectory() != null) {
env.put("base.dir", applyCwd(project.getDirectory()).getAbsolutePath());
}
env.put("ceylon.home", System.getProperty(Constants.PROP_CEYLON_HOME_DIR));
//env.put("ceylon.system.repo", System.getProperty("ceylon.system.repo"));
env.put("ceylon.version.number", Versions.CEYLON_VERSION_NUMBER);
env.put("ceylon.version.major", Integer.toString(Versions.CEYLON_VERSION_MAJOR));
env.put("ceylon.version.minor", Integer.toString(Versions.CEYLON_VERSION_MINOR));
env.put("ceylon.version.release", Integer.toString(Versions.CEYLON_VERSION_RELEASE));
env.put("ceylon.version.name", Versions.CEYLON_VERSION_NAME);
Set seenKeys = new HashSet<>();
List vars = new LinkedList<>(project.getVariables());
while (!vars.isEmpty()) {
Variable var = vars.remove(0);
if (seenKeys.contains(var.getKey())) {
throw new RuntimeException("Variables for project do not form a tree");
}
seenKeys.add(var.getKey());
// TODO Use the value from rest if there is one, only prompting if
// there is not
// TODO The problem with this is: "How does the user figure out
// what option they need to specify on the command line
// in order to avoid being prompted for it interactively?"
// Each subtool could provide their own getters and setters
// but they requires we write a subtool for each project
// It would be nice if we didn't have to do that, but could just
// drive the whole thing from a script in the templates dir.
vars.addAll(0, var.initialize(getProjectName(), env));
}
String sourceFolder = Constants.DEFAULT_SOURCE_DIR;
String baseDir = env.get("base.dir");
if (project.getDirectory() == null) {
project.setDirectory(new File(baseDir));
}
try {
CeylonConfig config = CeylonConfig.createFromLocalDir(new File(baseDir));
List srcs = DefaultToolOptions.getCompilerSourceDirs(config);
if (!srcs.isEmpty()) {
sourceFolder = srcs.get(0).getPath();
} else {
sourceFolder = Constants.DEFAULT_SOURCE_DIR;
}
} catch (Exception e) {
// Ignore any errors
}
env.put("source.folder", sourceFolder);
log(env);
return env;
}
public Copy substituting(final String cwd, final String src, final String dst) {
return substituting(cwd, new PathMatcher() {
@Override
public boolean matches(Path path) {
return path.endsWith(src);
}
}, dst);
}
public Copy substituting(final String cwd, PathMatcher pathMatcher, String dst) {
return new Copy(new File(getFromDir(), cwd), applyCwd(project.getDirectory()), pathMatcher, dst, true);
}
private void log(Object msg) {
if (verbose != null) {
System.out.println(msg);
}
}
public abstract class BaseProject extends Project {
private final Variable baseDir;
private final Variable moduleName;
private final Variable moduleVersion;
private final Variable eclipseProjectName;
private final Variable eclipse;
private final Variable ant;
public BaseProject(String defBaseDir, String defModName) {
baseDir = Variable.directory("base.dir", shouldCreateProject() ? defBaseDir : ".");
moduleName = Variable.moduleName("module.name", defModName);
moduleVersion = Variable.moduleVersion("module.version", "1.0.0");
eclipseProjectName = new Variable("eclipse.project.name", null, new PromptedValue("eclipse.project.name", "@[module.name]"));
eclipse = Variable.yesNo("eclipse", Messages.msg("mnemonic.yes"), eclipseProjectName);
ant = Variable.yesNo("ant", Messages.msg("mnemonic.yes"));
}
@OptionArgument
@Description("Specifies the name for the new module.")
public void setModuleName(String moduleName) {
this.moduleName.setVariableValue(new GivenValue(moduleName));
}
@OptionArgument
@Description("Specifies the version for the new module.")
public void setModuleVersion(String moduleVersion) {
this.moduleVersion.setVariableValue(new GivenValue(moduleVersion));
}
@OptionArgument
@Description("Indicates if an Eclipse project should be generated or not.")
public void setEclipse(boolean eclipse) {
this.eclipse.setVariableValue(new GivenValue(Boolean.toString(eclipse)));
}
@OptionArgument
@Description("Specifies the name for the Eclipse project.")
public void setEclipseProjectName(String eclipseProjectName) {
this.eclipseProjectName.setVariableValue(new GivenValue(eclipseProjectName));
}
@OptionArgument
@Description("Indicates if an Ant build file should be generated or not.")
public void setAnt(boolean ant) {
this.ant.setVariableValue(new GivenValue(Boolean.toString(ant)));
}
@Override
public List getVariables() {
List result = new ArrayList<>();
if (getDirectory() == null) {
result.add(baseDir);
}
result.add(moduleName);
result.add(Variable.moduleQuotedName("module.quoted.name", "module.name"));
result.add(Variable.moduleDir("module.dir", "module.name"));
result.add(moduleVersion);
if (shouldCreateProject()) {
result.add(eclipse);
result.add(ant);
}
return result;
}
// Check if the target directory is not a project
protected boolean shouldCreateProject() {
// If it has a ".ceylon" folder we assume a project exists
File ceylonDir = new File(applyCwd(getDirectory()), ".ceylon");
if (ceylonDir.isDirectory()) {
return false;
}
// If there's no ".ceylon" folder but there is a "source" folder
// we also assume a project exists
File sourcesDir = new File(applyCwd(getDirectory()), Constants.DEFAULT_SOURCE_DIR);
if (sourcesDir.isDirectory()) {
return false;
}
return true;
}
@Override
public List getResources(Environment env) {
FileSystem fs = FileSystems.getDefault();
List result = new ArrayList<>();
result.add(substituting("source",
fs.getPathMatcher("glob:**"),
new Template("@[source.folder]/@[module.dir]").eval(env)));
if ("true".equals(env.get("ant"))) {
result.add(substituting("ant", "build.xml", "."));
}
if ("true".equals(env.get("eclipse"))) {
result.add(substituting("eclipse",
fs.getPathMatcher("glob:**"),
"."));
}
return result;
}
}
@Description("Generates a 'Hello World' style project." +
"\n\n" +
"Takes a `` argument to indicate in which directory the new project should be created.")
public class HelloWorld extends BaseProject {
public HelloWorld() {
super("helloworld", "com.example.helloworld");
}
}
@Description("Generates a very simple empty project" +
"\n\n" +
"Takes a `` argument to indicate in which directory the new project should be created.")
public class Simple extends BaseProject {
public Simple() {
super("simple", "com.example.simple");
}
}
@Description("Generates a project that is able to use Java legacy code" +
"\n\n" +
"Takes a `` argument to indicate in which directory the new project should be created.")
public class JavaInterop extends BaseProject {
public JavaInterop() {
super("javainterop", "com.example.javainterop");
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy