1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package com.sun.tools.jextract;
  24 
  25 import jdk.internal.joptsimple.OptionException;
  26 import jdk.internal.joptsimple.OptionParser;
  27 import jdk.internal.joptsimple.OptionSet;
  28 import jdk.internal.joptsimple.util.KeyValuePair;
  29 
  30 import java.io.IOException;
  31 import java.nio.file.Files;
  32 import java.nio.file.Path;
  33 import java.nio.file.Paths;
  34 import java.text.MessageFormat;
  35 import java.util.logging.ConsoleHandler;
  36 import java.util.logging.Level;
  37 import java.util.logging.Logger;
  38 import java.util.logging.SimpleFormatter;
  39 import java.util.Locale;
  40 import java.util.ResourceBundle;
  41 
  42 public final class Main {
  43     public static final boolean DEBUG = Boolean.getBoolean("jextract.debug");
  44 
  45     // FIXME: Remove this if/when the macros support is deemed stable
  46     public static boolean INCLUDE_MACROS = Boolean.parseBoolean(System.getProperty("jextract.INCLUDE_MACROS", "true"));
  47 
  48     private static final String MESSAGES_RESOURCE = "com.sun.tools.jextract.resources.Messages";
  49 
  50     private static final ResourceBundle MESSAGES_BUNDLE;
  51     static {
  52         MESSAGES_BUNDLE = ResourceBundle.getBundle(MESSAGES_RESOURCE, Locale.getDefault());
  53     }
  54 
  55     public static String format(String msgId, Object... args) {
  56         return new MessageFormat(MESSAGES_BUNDLE.getString(msgId)).format(args);
  57     }
  58 
  59     final Context ctx;
  60     String targetPackage;
  61 
  62     public Main(Context ctx) {
  63         this.ctx = ctx;
  64     }
  65 
  66     private void processPackageMapping(Object arg) {
  67         String str = (String) arg;
  68         Path p = null;
  69         String pkgName;
  70         if (str.indexOf('=') == -1) {
  71             pkgName = str;
  72         } else {
  73             KeyValuePair kv = KeyValuePair.valueOf(str);
  74             p = Paths.get(kv.key);
  75             pkgName = kv.value;
  76 
  77             if (!Files.isDirectory(p)) {
  78                 throw new IllegalArgumentException(format("not.a.directory", kv.key));
  79             }
  80         }
  81 
  82         Validators.validPackageName(pkgName);
  83         ctx.usePackageForFolder(p, pkgName);
  84     }
  85 
  86     private void processHeader(Object header) {
  87         Path p = Paths.get((String) header);
  88         if (!Files.isReadable(p)) {
  89             throw new IllegalArgumentException(format("cannot.read.header.file", header));
  90         }
  91         p = p.toAbsolutePath();
  92         ctx.usePackageForFolder(p.getParent(), targetPackage);
  93         ctx.sources.add(p);
  94     }
  95 
  96     private void setupLogging(Level level) {
  97         Logger logger = ctx.logger;
  98         logger.setUseParentHandlers(false);
  99         ConsoleHandler log = new ConsoleHandler();
 100         System.setProperty("java.util.logging.SimpleFormatter.format", "%4$s: %5$s%n");
 101         log.setFormatter(new SimpleFormatter());
 102         logger.setLevel(level);
 103         log.setLevel(level);
 104         logger.addHandler(log);
 105     }
 106 
 107     private void printHelpAndExit(OptionParser parser) {
 108         try {
 109             parser.printHelpOn(System.err);
 110         } catch (IOException ex) {
 111             if (Main.DEBUG) {
 112                 ex.printStackTrace(System.err);
 113             }
 114         }
 115         System.exit(1);
 116     }
 117 
 118     public void run(String[] args) {
 119         OptionParser parser = new OptionParser();
 120         parser.accepts("dry-run", format("help.dry_run"));
 121         parser.accepts("I", format("help.I")).withRequiredArg();
 122         parser.accepts("L", format("help.L")).withRequiredArg();
 123         parser.accepts("l", format("help.l")).withRequiredArg();
 124         parser.accepts("o", format("help.o")).withRequiredArg();
 125         parser.accepts("t", format("help.t")).withRequiredArg();
 126         parser.accepts("m", format("help.m")).withRequiredArg();
 127         parser.accepts("h", format("help.h")).forHelp();
 128         parser.accepts("help", format("help.h")).forHelp();
 129         parser.accepts("C", format("help.C")).withRequiredArg();
 130         parser.accepts("log", format("help.log")).withRequiredArg();
 131         parser.accepts("?", format("help.h")).forHelp();
 132         parser.nonOptions(format("help.non.option"));
 133 
 134         OptionSet options = null;
 135         try {
 136              options = parser.parse(args);
 137         } catch (OptionException oe) {
 138              System.err.println(oe.getMessage());
 139              if (Main.DEBUG) {
 140                  oe.printStackTrace(System.err);
 141              }
 142              printHelpAndExit(parser);
 143         }
 144 
 145         if (args.length == 0 || options.has("h") || options.has("?") || options.has("help")) {
 146              printHelpAndExit(parser);
 147         }
 148 
 149         if (options.has("log")) {
 150             setupLogging(Level.parse((String) options.valueOf("log")));
 151         } else {
 152             setupLogging(Level.WARNING);
 153         }
 154 
 155         if (options.has("I")) {
 156             options.valuesOf("I").forEach(p -> ctx.clangArgs.add("-I" + p));
 157         }
 158 
 159         if (options.has("C")) {
 160             options.valuesOf("C").forEach(p -> ctx.clangArgs.add((String) p));
 161         }
 162 
 163         if (options.has("l")) {
 164             options.valuesOf("l").forEach(p -> ctx.libraries.add((String) p));
 165         }
 166 
 167         targetPackage = options.has("t") ? (String) options.valueOf("t") : "";
 168         if (!targetPackage.isEmpty()) {
 169             Validators.validPackageName(targetPackage);
 170         }
 171 
 172         if (options.has("m")) {
 173             options.valuesOf("m").forEach(this::processPackageMapping);
 174         }
 175 
 176         try {
 177             options.nonOptionArguments().stream().forEach(this::processHeader);
 178             ctx.parse(AsmCodeFactory::new);
 179         } catch (RuntimeException re) {
 180             System.err.println(re.getMessage());
 181             if (Main.DEBUG) {
 182                 re.printStackTrace(System.err);
 183             }
 184             System.exit(2);
 185         }
 186 
 187         if (options.has("dry-run")) {
 188             System.exit(0);
 189         }
 190 
 191         String outputName = options.has("o")? (String)options.valueOf("o") :
 192             options.nonOptionArguments().get(0) + ".jar";
 193         Path jar = Paths.get(outputName);
 194         try {
 195             ctx.collectJarFile(jar, targetPackage);
 196         } catch (IOException ex) {
 197             System.err.println(format("cannot.write.jar.file", jar, ex));
 198             if (Main.DEBUG) {
 199                 ex.printStackTrace(System.err);
 200             }
 201             System.exit(3);
 202         }
 203     }
 204 
 205     public static void main(String... args) {
 206         Main instance = new Main(Context.getInstance());
 207 
 208         instance.run(args);
 209     }
 210 
 211 }