Package com.redhat.ceylon.compiler

Source Code of com.redhat.ceylon.compiler.CeylonCompileTool$Helper

/*
* 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.compiler;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.redhat.ceylon.cmr.ceylon.OutputRepoUsingTool;
import com.redhat.ceylon.common.Constants;
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.Hidden;
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.ToolUsageError;
import com.redhat.ceylon.common.tools.ModuleWildcardsHelper;
import com.redhat.ceylon.common.tools.SourceArgumentsResolver;
import com.redhat.ceylon.compiler.java.launcher.Main;
import com.redhat.ceylon.compiler.java.launcher.Main.ExitState.CeylonState;
import com.redhat.ceylon.compiler.typechecker.analyzer.Warning;
import com.sun.tools.javac.main.JavacOption;
import com.sun.tools.javac.main.OptionName;
import com.sun.tools.javac.main.RecognizedOptions;
import com.sun.tools.javac.main.RecognizedOptions.OptionHelper;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Options;

@Summary("Compiles Ceylon and Java source code and directly produces module " +
    "and source archives in a module repository.")
@Description("The default module repositories are `modules` and `" +
    Constants.REPO_URL_CEYLON+"`, while the default source directory is `source` " +
    "and the default resource directory is `resource`. " +
    "The default output module repository is `modules`." +
    "\n\n" +
    "The `<moduleOrFile>` arguments can be either module names (without versions) " +
    "or file paths specifying the Ceylon or Java source code to compile." +
    "\n\n" +
    "When `<moduleOrFile>` specifies a module the compiler searches for " +
    "compilation units and resource files belonging to the specified modules " +
    "in the specified source and resource directories. " +
    "For each specified module, the compiler generates a module archive, " +
    "source archive, and their checksum files in the specified output module " +
    "repository." +
    "\n\n"+
    "When `<moduleOrFile>` specifies a source file only that file is compiled and " +
    "the module archive is created or updated with the .class files produced. " +
    "The source file path is treated as relative to the current directory " +
    "(it still needs to be located either in the default source folder or in " +
    "any folder defined by the configuration file or `--source` options!)."+
        "\n\n" +
        "When `<moduleOrFile>` specifies a resource file only that file is added to " +
        "the module archive. " +
        "The resource file path is treated as relative to the current directory " +
        "(it still needs to be located either in the default resource folder or in " +
        "any folder defined by the configuration file or `--resource` options!)."+
        "\n\n" +
        "All program elements imported by a compilation unit must belong to the " +
        "same module as the compilation unit, or must belong to a module that " +
        "is explicitly imported in the module descriptor." +
        "\n\n" +
        "The compiler searches for dependencies in the following locations:" +
        "\n\n" +
        "* module archives in the specified repositories,\n"+
        "* source archives in the specified repositories, and\n"+
        "* module directories in the specified source directories.\n")
@RemainingSections(
        OutputRepoUsingTool.DOCSECTION_CONFIG_COMPILER +
        "\n\n" +
        OutputRepoUsingTool.DOCSECTION_REPOSITORIES +
        "\n\n" +
        "## Specifying `javac` options" +
        "\n\n" +
        "It is possible to pass options to the `javac` compiler by prefixing them " +
        "with `--javac=` and separating the javac option from its argument (if any) " +
        "using another `=`. For example:" +
        "\n\n" +
        "* The option `--javac=-target=1.6` is equivalent to `javac`'s `-target 1.6` and,\n" +
        "* the option `--javac=-g:none` is equivalent to `javac`'s `-g:none`" +
        "\n\n" +
        "Execute `ceylon compile --javac=-help` for a list of the standard javac " +
        "options, and ceylon compile --javac=-X for a list of the non-standard javac " +
        "options." +
        "\n\n" +
        "**Important note**: There is no guarantee that any particular `javac` " +
        "option or combination of options will work, or continue to work in " +
        "future releases.")
public class CeylonCompileTool extends OutputRepoUsingTool {

    private static final class Helper implements OptionHelper {
        String lastError;

        @Override
        public void setOut(PrintWriter out) {
           
        }

        @Override
        public void printXhelp() {
           
        }

        @Override
        public void printVersion() {
           
        }

        @Override
        public void printHelp() {
           
        }

        @Override
        public void printFullVersion() {
           
        }

        @Override
        public void error(String key, Object... args) {
            lastError = Main.getLocalizedString(key, args);
        }

        @Override
        public void addFile(File f) {
        }

        @Override
        public void addClassName(String s) {
        }
    }
   
    private static final Helper HELPER = new Helper();

    private List<File> sources = DefaultToolOptions.getCompilerSourceDirs();
    private List<File> resources = DefaultToolOptions.getCompilerResourceDirs();
    private List<String> modulesOrFiles = Arrays.asList("*");
    private boolean continueOnErrors;
    private List<String> javac = Collections.emptyList();
    private String encoding;
    private String resourceRoot = DefaultToolOptions.getCompilerResourceRootName();
    private boolean noOsgi = DefaultToolOptions.getCompilerNoOsgi();
    private boolean noPom = DefaultToolOptions.getCompilerNoPom();
    private boolean pack200 = DefaultToolOptions.getCompilerPack200();
    private String suppressWarnings = DefaultToolOptions.getCompilerSuppressWarnings();

    public CeylonCompileTool() {
        super(CeylonCompileMessages.RESOURCE_BUNDLE);
    }
   
    @Option(longName="no-osgi")
    @Description("Indicates that the generated car file should not contain OSGi module declarations.")
    public void setNoOsgi(boolean noOsgi) {
        this.noOsgi = noOsgi;
    }

    @Option(longName="no-pom")
    @Description("Indicates that the generated car file should not contain Maven POM module declarations.")
    public void setNoPom(boolean noPom) {
        this.noPom = noPom;
    }

    @Option(longName="pack200")
    @Description("Try to make the generated car file smaller by repacking it using `pack200`.")
    public void setPack200(boolean pack200) {
        this.pack200 = pack200;
    }

    @OptionArgument(shortName='s', longName="src", argumentName="dirs")
    @ParsedBy(StandardArgumentParsers.PathArgumentParser.class)
    @Description("Path to directory containing source files. " +
            "Can be specified multiple times; you can also specify several " +
            "paths separated by your operating system's `PATH` separator." +
            " (default: `./source`)")
    public void setSrc(List<File> source) {
        this.sources = source;
    }
   
    @OptionArgument(longName="source", argumentName="dirs")
    @ParsedBy(StandardArgumentParsers.PathArgumentParser.class)
    @Description("An alias for `--src`" +
            " (default: `./source`)")
    public void setSource(List<File> source) {
        setSrc(source);
    }
   
    @OptionArgument(shortName='r', longName="resource", argumentName="dirs")
    @ParsedBy(StandardArgumentParsers.PathArgumentParser.class)
    @Description("Path to directory containing resource files. " +
            "Can be specified multiple times; you can also specify several " +
            "paths separated by your operating system's `PATH` separator." +
            " (default: `./resource`)")
    public void setResource(List<File> resource) {
        this.resources = resource;
    }

    @OptionArgument(shortName='R', argumentName="folder-name")
    @Description("Sets the special resource folder name whose files will " +
            "end up in the root of the resulting module CAR file (default: ROOT).")
    public void setResourceRoot(String resourceRoot) {
        this.resourceRoot = resourceRoot;
    }
   
    @Hidden
    @Option(longName="continue-on-errors")
    @Description("Set to continue compiling even when errors are found.")
    public void setContinueOnErrors(boolean continueOnErrors) {
        this.continueOnErrors = continueOnErrors;
    }

    @OptionArgument(shortName='E', argumentName="encoding")
    @Description("Sets the encoding used for reading source files" +
            "(default: platform-specific).")
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    @Argument(argumentName="moduleOrFile", multiplicity="*")
    public void setModule(List<String> moduleOrFile) {
        this.modulesOrFiles = moduleOrFile;
    }
   
    @Option(shortName='d')
    @OptionArgument(argumentName = "flags")
    @Description("Produce verbose output. " +
            "If no `flags` are given then be verbose about everything, " +
            "otherwise just be verbose about the flags which are present. " +
            "Allowed flags include: `all`, `loader`, `ast`, `code`, `cmr`, `benchmark`.")
    public void setVerbose(String verbose) {
        super.setVerbose(verbose);
    }
   
    @OptionArgument(argumentName="option")
    @Description("Passes an option to the underlying java compiler.")
    public void setJavac(List<String> javac) {
        this.javac = javac;
    }
   
    @Option
    @OptionArgument(argumentName = "warnings")
    @Description("Suppress the reporting of the given warnings. " +
            "If no `warnings` are given then suppresss the reporting of all warnings, " +
            "otherwise just suppresss those which are present. " +
            "Allowed flags include: " +
            "`filenameNonAscii`, `filenameClaselessCollision`, `deprecation`, "+
            "`compilerAnnotation`, `doclink`, `expressionTypeNothing`, "+
            "`unusedDeclaration`, `unusedImport`, `ceylonNamespace`, "+
            "`javaNamespace`, `suppressedAlready`, `suppressesNothing`.")
    public void setSuppressWarnings(String warnings) {
        if (warnings != null
                && !warnings.isEmpty()) {
            for (String warningName : warnings.trim().split(" *, *")) {
                try {
                    Warning.valueOf(warningName);
                } catch (IllegalArgumentException e) {
                    StringBuffer sb = new StringBuffer();
                    for (Warning w : Warning.values()) {
                        sb.append(w.name()).append(", ");
                    }
                    sb.setLength(sb.length() - 2);
                    throw new IllegalArgumentException(CeylonCompileMessages.msg(
                            "option.error.syntax.suppress.warnings",
                            warningName, sb.toString()));
                }
            }
        }
        this.suppressWarnings = warnings;
    }

    private List<String> arguments;
   
    private Main compiler;
   
    private static void validateWithJavac(Options options, JavacOption encodingOpt, String option, String argument, String key) {
        if (!encodingOpt.matches(option)) {
            throw new IllegalArgumentException(CeylonCompileMessages.msg(key, option));
        }
        HELPER.lastError = null;
        if (encodingOpt.hasArg()) {
            if (encodingOpt.process(options, option, argument)
                    || HELPER.lastError != null) {
                throw new IllegalArgumentException(HELPER.lastError);
            }
        } else {
            if (encodingOpt.process(options, option)
                    || HELPER.lastError != null) {
                throw new IllegalArgumentException(HELPER.lastError);
            }
        }
    }
   
    @Override
    public void initialize() throws IOException {
        setSystemProperties();
        compiler = new Main("ceylon compile");
        Options options = Options.instance(new Context());
       
        if (modulesOrFiles.isEmpty() &&
                !javac.contains("-help") &&
                !javac.contains("-X") &&
                !javac.contains("-version")) {
            throw new IllegalStateException("Argument moduleOrFile should appear at least 1 time(s)");
        }
       
        arguments = new ArrayList<>();
       
        if (cwd != null) {
            arguments.add("-cwd");
            arguments.add(cwd.getPath());
        }
       
        for (File source : applyCwd(this.sources)) {
            arguments.add("-src");
            arguments.add(source.getPath());
            options.addMulti(OptionName.SOURCEPATH, source.getPath());
        }
       
        for (File resource : applyCwd(this.resources)) {
            arguments.add("-res");
            arguments.add(resource.getPath());
            //options.addMulti(OptionName.RESOURCEPATH, resource.getPath());
        }
       
        if (resourceRoot != null) {
            arguments.add("-resroot");
            arguments.add(resourceRoot);
        }
       
        if (continueOnErrors) {
            arguments.add("-continue");
        }
       
        if (offline) {
            arguments.add("-offline");
        }
       
        if (mavenOverrides != null) {
            arguments.add("-maven-overrides");
            if (mavenOverrides.startsWith("classpath:")) {
                arguments.add(mavenOverrides);
            } else {
                arguments.add(applyCwd(new File(mavenOverrides)).getPath());
            }
        }

        if (noOsgi) {
            arguments.add("-noosgi");
        }

        if (noPom) {
            arguments.add("-nopom");
        }

        if (pack200) {
            arguments.add("-pack200");
        }
       
        if (verbose != null) {
            if (verbose.isEmpty()) {
                arguments.add("-verbose");
            } else {
                arguments.add("-verbose:" + verbose);
            }
        }
       
        if (out != null) {
            arguments.add("-out");
            arguments.add(out);
        }
       
        if (user != null) {
            arguments.add("-user");
            arguments.add(user);
        }
        if (pass != null) {
            arguments.add("-pass");
            arguments.add(pass);
        }

        String fileEncoding = encoding;
        if (fileEncoding == null) {
            fileEncoding = DefaultToolOptions.getDefaultEncoding();
        }
        if (fileEncoding != null) {
            JavacOption encodingOpt = getJavacOpt(OptionName.ENCODING.toString());
            validateWithJavac(options, encodingOpt, OptionName.ENCODING.toString(), fileEncoding, "option.error.syntax.encoding");
            arguments.add(OptionName.ENCODING.toString());
            arguments.add(fileEncoding);
        }

        if (systemRepo != null) {
            arguments.add("-sysrep");
            arguments.add(systemRepo);
        }
       
        if (cacheRepo != null) {
            arguments.add("-cacherep");
            arguments.add(cacheRepo);
        }
       
        if (noDefRepos) {
            arguments.add("-nodefreps");
        }
       
        if (repo != null) {
            for (URI uri : this.repo) {
                arguments.add("-rep");
                arguments.add(uri.toString());
            }
        }
       
        if (suppressWarnings != null) {
            arguments.add("-suppress-warnings");
            arguments.add(suppressWarnings);
        }
       
        addJavacArguments(arguments);
       
        List<File> srcs = applyCwd(this.sources);
        List<String> expandedModulesOrFiles = ModuleWildcardsHelper.expandWildcards(srcs , this.modulesOrFiles);
        if (expandedModulesOrFiles.isEmpty()) {
            throw new ToolUsageError("No modules or source files to compile");
        }
       
        JavacOption sourceFileOpt = getJavacOpt(OptionName.SOURCEFILE.toString());
        if (sourceFileOpt != null) {
            for (String moduleOrFile : expandedModulesOrFiles) {
                validateWithJavac(options, sourceFileOpt, moduleOrFile, moduleOrFile, "argument.error");
            }
        }
       
        validateSourceArguments(expandedModulesOrFiles);
       
        arguments.addAll(expandedModulesOrFiles);
       
        if (verbose != null) {
            System.out.println(arguments);
            System.out.flush();
        }
    }

    private void validateSourceArguments(List<String> modulesOrFiles) throws IOException {
        SourceArgumentsResolver resolver = new SourceArgumentsResolver(this.sources, this.resources, Constants.CEYLON_SUFFIX, Constants.JAVA_SUFFIX);
        resolver.cwd(cwd).parse(modulesOrFiles);
    }
   
    private static JavacOption getJavacOpt(String optionName) {
        for (com.sun.tools.javac.main.JavacOption o : RecognizedOptions.getJavaCompilerOptions(HELPER)) {
            if (optionName.equals(o.getName().toString())) {
                return o;
            }
        }
        return null;
    }

    /**
     * Run the compilation
     * @throws IOException
     * @throws CompilerErrorException If the source code had errors
     * @throws SystemErrorException If there was a system error
     * @throws CompilerBugException If a bug in the compiler was detected.
     */
    @Override
    public void run() throws IOException {
        int result = compiler.compile(arguments.toArray(new String[arguments.size()]));
        handleExitCode(result, compiler.exitState);
    }

    private void handleExitCode(
            int javacExitCode,
            Main.ExitState exitState) {
        if (exitState == null) {
            throw new IllegalStateException("Missing ExitState, " + javacExitCode);
        }
        CeylonState ceylonState = exitState.ceylonState;
        switch (ceylonState) {
        case OK:
            break;
        case ERROR:
            throw new CompilerErrorException(exitState.errorCount);
        case SYS:
            throw new SystemErrorException(exitState.abortingException);
        case BUG:
            throw new CompilerBugException(exitState);
        default:
            throw new IllegalStateException("Unexpected CeylonState " + ceylonState);
        }
    }

    private void addJavacArguments(List<String> arguments) {
        Options options = Options.instance(new Context());
        for (String argument : javac) {
            HELPER.lastError = null;
            String value = null;
            int index = argument.indexOf('=');
            if (index != -1) {
                value = index < argument.length() ? argument.substring(index+1) : "";
                argument = argument.substring(0, index);
            }
           
            JavacOption javacOpt = getJavacOpt(argument.replaceAll(":.*", ":"));
            if (javacOpt == null) {
                throw new IllegalArgumentException(CeylonCompileMessages.msg("option.error.javac", argument));
            }
           
           
            if (value != null) {
                if (!javacOpt.hasArg()) {
                    throw new IllegalArgumentException(CeylonCompileMessages.msg("option.error.syntax.javac", argument, "Unexpected argument given"));
                }
                if (!javacOpt.matches(argument)) {
                    throw new IllegalArgumentException(CeylonCompileMessages.msg("option.error.javac", argument));
                }
                if (javacOpt.process(options, argument, value)) {
                    throw new IllegalArgumentException(CeylonCompileMessages.msg("option.error.syntax.javac", argument, HELPER.lastError));
                }
               
           
            } else {
                if (javacOpt.hasArg()) {
                    throw new IllegalArgumentException(CeylonCompileMessages.msg("option.error.syntax.javac", argument, "Missing expected argument"));
                }
                if (!javacOpt.matches(argument)) {
                    throw new IllegalArgumentException(CeylonCompileMessages.msg("option.error.javac", argument));
                }
                if (javacOpt.process(options, argument)) {
                    throw new IllegalArgumentException(CeylonCompileMessages.msg("option.error.syntax.javac", argument, HELPER.lastError));
                }
            }
           
            arguments.add(argument);
            if (value != null) {
                arguments.add(value);
            }
        }
       
   
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.CeylonCompileTool$Helper

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.