Package com.redhat.ceylon.tools.plugin

Source Code of com.redhat.ceylon.tools.plugin.CeylonPluginTool$CeylonPluginException

package com.redhat.ceylon.tools.plugin;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.ArtifactResult;
import com.redhat.ceylon.cmr.api.ModuleQuery;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.cmr.ceylon.OutputRepoUsingTool;
import com.redhat.ceylon.cmr.impl.IOUtils;
import com.redhat.ceylon.cmr.impl.ShaSigner;
import com.redhat.ceylon.common.Constants;
import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.common.OSUtil;
import com.redhat.ceylon.common.config.DefaultToolOptions;
import com.redhat.ceylon.common.tool.Argument;
import com.redhat.ceylon.common.tool.Description;
import com.redhat.ceylon.common.tool.Option;
import com.redhat.ceylon.common.tool.OptionArgument;
import com.redhat.ceylon.common.tool.ParsedBy;
import com.redhat.ceylon.common.tool.RemainingSections;
import com.redhat.ceylon.common.tool.StandardArgumentParsers;
import com.redhat.ceylon.common.tool.Summary;
import com.redhat.ceylon.common.tool.ToolError;
import com.redhat.ceylon.common.tools.ModuleSpec;
import com.redhat.ceylon.compiler.typechecker.model.Module;

@Summary("Manages Ceylon command-line plugins")
@Description(
        "There are four modes of action:\n"+
        "\n"+
        "- `list`: will list the scripts installed\n"+
        "- `install`: installs the scripts for the modules listed on the command line. The scripts must either be packed in a\n"+
        "repository, or found in the current script folders\n"+
        "- `uninstall`: uninstalls the scripts for the modules listed on the command line\n"+
        "- `pack`: packs the scripts for the modules listed on the command line and publishes them to the specified output repository\n"+
        "\n"+
        "The list of modules specified can have their versions set, but if missing we will try to find the\n"+
        "version from the current source path. If the list of modules is omitted, we will use the list of\n"+
        "modules found in the current source path."
)
@RemainingSections(
"##EXAMPLE\n" +
"\n" +
"The following would list the plugins currently installed:\n" +
"\n" +
"    ceylon plugin list\n\n"+
"The following would install the ceylon.build plugins:\n" +
"\n" +
"    ceylon plugin install ceylon.build.engine\n"
)
public class CeylonPluginTool extends OutputRepoUsingTool {

    @SuppressWarnings("serial")
    public static class CeylonPluginException extends ToolError {
        public CeylonPluginException(String msgKey, Exception cause, Object... args) {
            super(CeylonPluginMessages.msg(msgKey, args), cause);
        }
        public CeylonPluginException(String msgKey, Object... args) {
            super(CeylonPluginMessages.msg(msgKey, args));
        }
    }

    public static enum Mode {
        pack, list, install, uninstall;
    }

    private List<ModuleSpec> modules;
    private List<File> scriptFolders = DefaultToolOptions.getCompilerScriptDirs();
    private List<File> sourceFolders = DefaultToolOptions.getCompilerSourceDirs();
    private boolean force;
    private boolean system;
    private Mode mode;

    public CeylonPluginTool() {
        super(CeylonPluginMessages.RESOURCE_BUNDLE);
    }
   
    @Argument(order = 1, argumentName="module", multiplicity="*")
    public void setModules(List<String> modules) {
        setModuleSpecs(ModuleSpec.parseEachList(modules));
    }
   
    public void setModuleSpecs(List<ModuleSpec> modules) {
        this.modules = modules;
    }
   
    @Argument(order = 0, argumentName="mode", multiplicity = "1")
    public void setMode(Mode mode) {
        this.mode = mode;
    }

    @Option(shortName='f')
    @Description("Force installation even if a previous version exists")
    public void setForce(boolean force) {
        this.force = force;
    }

    @Option
    @Description("Install to or uninstall from the system folder")
    public void setSystem(boolean system) {
        this.system = system;
    }

    @OptionArgument(shortName='s', longName="src", argumentName="dirs")
    @ParsedBy(StandardArgumentParsers.PathArgumentParser.class)
    @Description("A directory containing Ceylon and/or Java/JavaScript source code (default: `./source`)")
    public void setSrcFolders(List<File> sourceFolders) {
        this.sourceFolders = sourceFolders;
    }
   
    @OptionArgument(longName="source", argumentName="dirs")
    @ParsedBy(StandardArgumentParsers.PathArgumentParser.class)
    @Description("An alias for `--src`" +
            " (default: `./source`)")
    public void setSourceFolders(List<File> source) {
        setSrcFolders(source);
    }
   
    @OptionArgument(shortName='x', longName="script", argumentName="dirs")
    @ParsedBy(StandardArgumentParsers.PathArgumentParser.class)
    @Description("A directory containing your module documentation (default: `./script`)")
    public void setScriptFolders(List<File> scriptFolders) {
        this.scriptFolders = scriptFolders;
    }

    @Override
    public void run() throws Exception {
        setSystemProperties();
       
        // make sure we have a list of modules to work on if required
        if(modules == null)
            modules = new ArrayList<ModuleSpec>();
        boolean errorIfMissing = true;
        if(mode != Mode.list){
            if(modules.isEmpty()){
                modules.addAll(getSourceModules(applyCwd(sourceFolders)));
                errorIfMissing = false;
            }
            if(modules.isEmpty()){
                throw new CeylonPluginException("error.no.module.specified", sourceFolders);
            }
        }

        boolean worked = false;
        switch(mode){
        case pack:
        {
            RepositoryManager outputRepositoryManager = getOutputRepositoryManager();
            for(ModuleSpec module : modules){
                worked |= addScripts(outputRepositoryManager, module, errorIfMissing);
            }
            break;
        }
        case install:
        {
            RepositoryManager repositoryManager = getRepositoryManager();
            for(ModuleSpec module : modules){
                worked |= installScripts(repositoryManager, module, errorIfMissing);
            }
            break;
        }
        case uninstall:
        {
            for(ModuleSpec module : modules){
                worked |= uninstallScripts(module, errorIfMissing);
            }
            break;
        }
        case list:
            listScripts();
            worked = true;
            break;
        }
        if(!worked)
            throw new CeylonPluginException("error.no.script.found");
        flush();
    }

    private void listScripts() throws IOException {
        ArrayList<String> scripts = new ArrayList<String>();
        // Look in /etc/ceylon/bin/ and /etc/ceylon/bin/{moduleName}
        File systemDir = new File(FileUtil.getSystemConfigDir(), Constants.CEYLON_BIN_DIR);
        listScripts(systemDir, scripts);
        // They are in ~/.ceylon/bin/ and ~/.ceylon/bin/{moduleName}
        File defUserDir = new File(FileUtil.getDefaultUserDir(), Constants.CEYLON_BIN_DIR);
        listScripts(defUserDir, scripts);
        Collections.sort(scripts);
        for (String script : scripts) {
            append(script);
            newline();
        }
    }

    private void listScripts(File dir, List<String> scripts) throws IOException {
        File[] children = dir.listFiles();
        if (children != null) {
            for (File child : children) {
                if (child.isDirectory()) {
                    File[] modfiles = child.listFiles();
                    for (File f : modfiles) {
                        if (isScript(f)) {
                            scripts.add(f.getName() + " (" + child.getName() + ")");
                        }
                    }
                } else if (isScript(child)) {
                    scripts.add(child.getName());
                }
            }
        }
    }

    private boolean isScript(File f) {
        if (f.isFile() && f.getName().startsWith("ceylon-") && f.canExecute()) {
            boolean isWinScript = OSUtil.isWindows() && f.getName().toLowerCase().endsWith(".bat");
            boolean isUnixScript = !OSUtil.isWindows() && !f.getName().toLowerCase().endsWith(".bat");
            return isWinScript || isUnixScript;
        }
        return false;
    }
   
    private boolean installScripts(RepositoryManager repositoryManager, ModuleSpec module, boolean errorIfMissing) throws IOException {
        String version = module.getVersion();
        if((version == null || version.isEmpty()) && !module.getName().equals(Module.DEFAULT_MODULE_NAME)){
            version = checkModuleVersionsOrShowSuggestions(getRepositoryManager(), module.getName(), null, ModuleQuery.Type.ALL, null, null);
            if(version == null)
                return false;
        }
       
        File zipSource = null;
        List<File> existingScriptFolders = null;
        if(isSourceModule(module.getName(), version, applyCwd(sourceFolders))){
            // copy it directly from the source
            existingScriptFolders = findExistingScriptFolders(module.getName(), errorIfMissing);
           
            if(existingScriptFolders.isEmpty()){
                return false;
            }
        }else{
            // obtain it from the repo
            ArtifactContext context = new ArtifactContext(module.getName(), version, ArtifactContext.SCRIPTS_ZIPPED);
            ArtifactResult result = repositoryManager.getArtifactResult(context);
            if(result == null){
                String err = getModuleNotFoundErrorMessage(repositoryManager, module.getName(), version);
                errorAppend(err);
                errorNewline();
                return false;
            }
            zipSource = result.artifact();
        }

        File moduleScriptDir = getModuleScriptDir(module);
        if(moduleScriptDir.exists()){
            if(force)
                FileUtil.delete(moduleScriptDir);
            else{
                errorMsg("error.module.already.installed", module.getName(), moduleScriptDir);
                return false;
            }
        }
        if(!moduleScriptDir.mkdirs()){
            errorMsg("error.unable.create.dest.dir", moduleScriptDir);
            return false;
        }
        if(zipSource != null)
            extractScripts(zipSource, moduleScriptDir);
        else{
            for(File root : existingScriptFolders){
                FileUtil.copyAll(root, moduleScriptDir);
            }
        }
        msg("success.installed", module.getName(), moduleScriptDir);
        newline();
        return true;
    }

    private boolean uninstallScripts(ModuleSpec module, boolean errorIfMissing) throws IOException {
        File moduleScriptDir = getModuleScriptDir(module);
        if(moduleScriptDir.exists()){
            FileUtil.delete(moduleScriptDir);
            msg("success.uninstalled", module.getName(), moduleScriptDir);
            newline();
            return true;
        }else{
            if(errorIfMissing)
                errorMsg("error.no.script.installed.for.module", module.getName(), moduleScriptDir);
            return false;
        }
    }

    private File getModuleScriptDir(ModuleSpec module) {
        File installDir;
        if (system) {
            // Put them in /etc/ceylon/bin/{moduleName} (or equivalent on Windows / MacOS)
            installDir = FileUtil.getSystemConfigDir();
        } else {
            // Put them in ~/.ceylon/bin/{moduleName}
            installDir = FileUtil.getDefaultUserDir();
        }
        File binDir = new File(installDir, Constants.CEYLON_BIN_DIR);
        File moduleScriptDir = new File(binDir, module.getName());
        return moduleScriptDir;
    }

    private void extractScripts(File zip, File dir) throws IOException {
        try{
            IOUtils.extractArchive(zip, dir);
        }catch(IOUtils.UnzipException x){
            switch(x.failure){
            case CannotCreateDestination:
                throw new RuntimeException(CeylonPluginMessages.msg("error.unable.create.dest.dir", x.dir));
            case CopyError:
                throw new RuntimeException(CeylonPluginMessages.msg("error.unable.extract.entry", x.entryName, zip.getAbsolutePath()), x.getCause());
            case DestinationNotDirectory:
                throw new RuntimeException(CeylonPluginMessages.msg("error.not.dir.dest.dir", x.dir));
            default:
                throw x;
            }
        }
        // make all the files executable since we can't preserve that info the the zip file
        Files.walkFileTree(dir.toPath(), new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) throws IOException {
                if(Files.isRegularFile(path)){
                    path.toFile().setExecutable(true);
                }
                return FileVisitResult.CONTINUE;
            }
        });

    }

    private boolean addScripts(RepositoryManager outputRepositoryManager, ModuleSpec module, boolean errorIfMissing) throws IOException {
        String version = module.getVersion();
        if((version == null || version.isEmpty()) && !module.getName().equals(Module.DEFAULT_MODULE_NAME)){
            version = checkModuleVersionsOrShowSuggestions(getRepositoryManager(), module.getName(), null, ModuleQuery.Type.ALL, null, null);
            if(version == null)
                return false;
        }
        ArtifactContext artifactScriptsZip = new ArtifactContext(module.getName(), version, ArtifactContext.SCRIPTS_ZIPPED);
       
        // find all doc folders to zip
        List<File> existingScriptFolders = findExistingScriptFolders(module.getName(), errorIfMissing);
       
        if(existingScriptFolders.isEmpty()){
            return false;
        }

        // make the doc zip roots
        IOUtils.ZipRoot[] roots = new IOUtils.ZipRoot[existingScriptFolders.size()];
        int d=0;
        for(File scriptFolder : existingScriptFolders){
            roots[d] = new IOUtils.ZipRoot(scriptFolder, "");
        }
        File scriptZipFile = IOUtils.zipFolders(roots);
        File scriptZipSha1File = ShaSigner.sign(scriptZipFile, log, verbose != null);
       
        if(!repositoryRemoveArtifact(outputRepositoryManager, artifactScriptsZip)) return true;
        if(!repositoryRemoveArtifact(outputRepositoryManager, artifactScriptsZip.getSha1Context())) return true;
       
        if(!repositoryPutArtifact(outputRepositoryManager, artifactScriptsZip, scriptZipFile)) return true;
        if(!repositoryPutArtifact(outputRepositoryManager, artifactScriptsZip.getSha1Context(), scriptZipSha1File)) return true;
       
        scriptZipFile.delete();
        scriptZipSha1File.delete();

        msg("success.packed", module.getName());
        newline();

        return true;
    }

    private List<File> findExistingScriptFolders(String moduleName, boolean errorIfMissing) throws IOException {
        String scriptPath = moduleName.replace('.', File.separatorChar);
        List<File> existingScriptFolders = new ArrayList<File>(scriptFolders.size());
        for(File scriptFolder : scriptFolders){
            File moduleScriptFolder = new File(applyCwd(scriptFolder), scriptPath);
            if(moduleScriptFolder.exists() && FileUtil.containsFile(moduleScriptFolder))
                existingScriptFolders.add(moduleScriptFolder);
        }
        if(errorIfMissing && existingScriptFolders.isEmpty()){
            errorMsg("error.no.script.found.for.module", moduleName, scriptFolders, scriptPath);
        }
        return existingScriptFolders;
    }

    private boolean repositoryRemoveArtifact(RepositoryManager outputRepository, ArtifactContext artifactContext) throws IOException {
        try {
            outputRepository.removeArtifact(artifactContext);
            return true;
        } catch (Exception e) {
            errorMsg("error.failed.remove.artifact", artifactContext, e.getLocalizedMessage());
            return false;
        }
    }

    private boolean repositoryPutArtifact(RepositoryManager outputRepository, ArtifactContext artifactContext, File content) throws IOException {
        try {
            outputRepository.putArtifact(artifactContext, content);
            return true;
        } catch (Exception e) {
            errorMsg("error.failed.write.artifact", artifactContext, e.getLocalizedMessage());
            return false;
        }
    }

    @Override
    public void initialize() throws Exception {
    }
}
TOP

Related Classes of com.redhat.ceylon.tools.plugin.CeylonPluginTool$CeylonPluginException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.