001/*
002 * Anarres C Preprocessor
003 * Copyright (c) 2007-2015, Shevek
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
014 * or implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.anarres.cpp;
018
019import java.io.File;
020import java.io.PrintStream;
021import java.util.Arrays;
022import java.util.EnumSet;
023import java.util.List;
024import javax.annotation.Nonnull;
025import joptsimple.OptionParser;
026import joptsimple.OptionSet;
027import joptsimple.OptionSpec;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * (Currently a simple test class).
033 */
034public class Main {
035
036    private static final Logger LOG = LoggerFactory.getLogger(Main.class);
037
038    @Nonnull
039    private static CharSequence getWarnings() {
040        StringBuilder buf = new StringBuilder();
041        for (Warning w : Warning.values()) {
042            if (buf.length() > 0)
043                buf.append(", ");
044            String name = w.name().toLowerCase();
045            buf.append(name.replace('_', '-'));
046        }
047        return buf;
048    }
049
050    public static void main(String[] args) throws Exception {
051        (new Main()).run(args);
052    }
053
054    public void run(String[] args) throws Exception {
055
056        OptionParser parser = new OptionParser();
057        OptionSpec<?> helpOption = parser.accepts("help",
058                "Displays command-line help.")
059                .forHelp();
060        OptionSpec<?> versionOption = parser.acceptsAll(Arrays.asList("version"),
061                "Displays the product version (" + BuildMetadata.getInstance().getVersion() + ") and exits.")
062                .forHelp();
063
064        OptionSpec<?> debugOption = parser.acceptsAll(Arrays.asList("debug"),
065                "Enables debug output.");
066
067        OptionSpec<String> defineOption = parser.acceptsAll(Arrays.asList("define", "D"),
068                "Defines the given macro.")
069                .withRequiredArg().ofType(String.class).describedAs("name[=definition]");
070        OptionSpec<String> undefineOption = parser.acceptsAll(Arrays.asList("undefine", "U"),
071                "Undefines the given macro, previously either builtin or defined using -D.")
072                .withRequiredArg().describedAs("name");
073        OptionSpec<File> includeOption = parser.accepts("include",
074                "Process file as if \"#" + "include \"file\"\" appeared as the first line of the primary source file.")
075                .withRequiredArg().ofType(File.class).describedAs("file");
076        OptionSpec<File> incdirOption = parser.acceptsAll(Arrays.asList("incdir", "I"),
077                "Adds the directory dir to the list of directories to be searched for header files.")
078                .withRequiredArg().ofType(File.class).describedAs("dir");
079        OptionSpec<File> iquoteOption = parser.acceptsAll(Arrays.asList("iquote"),
080                "Adds the directory dir to the list of directories to be searched for header files included using \"\".")
081                .withRequiredArg().ofType(File.class).describedAs("dir");
082        OptionSpec<String> warningOption = parser.acceptsAll(Arrays.asList("warning", "W"),
083                "Enables the named warning class (" + getWarnings() + ").")
084                .withRequiredArg().ofType(String.class).describedAs("warning");
085        OptionSpec<Void> noWarningOption = parser.acceptsAll(Arrays.asList("no-warnings", "w"),
086                "Disables ALL warnings.");
087        OptionSpec<File> inputsOption = parser.nonOptions()
088                .ofType(File.class).describedAs("Files to process.");
089
090        OptionSet options = parser.parse(args);
091
092        if (options.has(helpOption)) {
093            parser.printHelpOn(System.out);
094            return;
095        }
096
097        if (options.has(versionOption)) {
098            version(System.out);
099            return;
100        }
101
102        Preprocessor pp = new Preprocessor();
103        pp.addFeature(Feature.DIGRAPHS);
104        pp.addFeature(Feature.TRIGRAPHS);
105        pp.addFeature(Feature.LINEMARKERS);
106        pp.addWarning(Warning.IMPORT);
107        pp.setListener(new DefaultPreprocessorListener());
108        pp.addMacro("__JCPP__");
109        pp.getSystemIncludePath().add("/usr/local/include");
110        pp.getSystemIncludePath().add("/usr/include");
111        pp.getFrameworksPath().add("/System/Library/Frameworks");
112        pp.getFrameworksPath().add("/Library/Frameworks");
113        pp.getFrameworksPath().add("/Local/Library/Frameworks");
114
115        if (options.has(debugOption))
116            pp.addFeature(Feature.DEBUG);
117
118        if (options.has(noWarningOption))
119            pp.getWarnings().clear();
120
121        for (String warning : options.valuesOf(warningOption)) {
122            warning = warning.toUpperCase();
123            warning = warning.replace('-', '_');
124            if (warning.equals("ALL"))
125                pp.addWarnings(EnumSet.allOf(Warning.class));
126            else
127                pp.addWarning(Enum.valueOf(Warning.class, warning));
128        }
129
130        for (String arg : options.valuesOf(defineOption)) {
131            int idx = arg.indexOf('=');
132            if (idx == -1)
133                pp.addMacro(arg);
134            else
135                pp.addMacro(arg.substring(0, idx), arg.substring(idx + 1));
136        }
137        for (String arg : options.valuesOf(undefineOption)) {
138            pp.getMacros().remove(arg);
139        }
140
141        for (File dir : options.valuesOf(incdirOption))
142            pp.getSystemIncludePath().add(dir.getAbsolutePath());
143        for (File dir : options.valuesOf(iquoteOption))
144            pp.getQuoteIncludePath().add(dir.getAbsolutePath());
145        for (File file : options.valuesOf(includeOption))
146            // Comply exactly with spec.
147            pp.addInput(new StringLexerSource("#" + "include \"" + file + "\"\n"));
148
149        List<File> inputs = options.valuesOf(inputsOption);
150        if (inputs.isEmpty()) {
151            pp.addInput(new InputLexerSource(System.in));
152        } else {
153            for (File input : inputs)
154                pp.addInput(new FileLexerSource(input));
155        }
156
157        if (pp.getFeature(Feature.DEBUG)) {
158            LOG.info("#" + "include \"...\" search starts here:");
159            for (String dir : pp.getQuoteIncludePath())
160                LOG.info("  " + dir);
161            LOG.info("#" + "include <...> search starts here:");
162            for (String dir : pp.getSystemIncludePath())
163                LOG.info("  " + dir);
164            LOG.info("End of search list.");
165        }
166
167        try {
168            for (;;) {
169                Token tok = pp.token();
170                if (tok == null)
171                    break;
172                if (tok.getType() == Token.EOF)
173                    break;
174                System.out.print(tok.getText());
175            }
176        } catch (Exception e) {
177            StringBuilder buf = new StringBuilder("Preprocessor failed:\n");
178            Source s = pp.getSource();
179            while (s != null) {
180                buf.append(" -> ").append(s).append("\n");
181                s = s.getParent();
182            }
183            LOG.error(buf.toString(), e);
184        }
185
186    }
187
188    private static void version(@Nonnull PrintStream out) {
189        BuildMetadata metadata = BuildMetadata.getInstance();
190        out.println("Anarres Java C Preprocessor version " + metadata.getVersion() + " change-id " + metadata.getChangeId());
191        out.println("Copyright (C) 2007-2015 Shevek (http://www.anarres.org/).");
192        out.println("This is free software; see the source for copying conditions.  There is NO");
193        out.println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
194    }
195}