1 /* 2 * Copyright (c) 2020, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.incubator.jextract.tool; 27 28 import jdk.incubator.jextract.Declaration; 29 import jdk.incubator.jextract.JextractTask; 30 import jdk.internal.joptsimple.OptionException; 31 import jdk.internal.joptsimple.OptionParser; 32 import jdk.internal.joptsimple.OptionSet; 33 import jdk.internal.joptsimple.util.KeyValuePair; 34 35 import javax.tools.JavaFileObject; 36 import java.io.File; 37 import java.io.IOException; 38 import java.io.PrintWriter; 39 import java.nio.file.Files; 40 import java.nio.file.Path; 41 import java.nio.file.Paths; 42 import java.text.MessageFormat; 43 import java.util.List; 44 import java.util.Locale; 45 import java.util.ResourceBundle; 46 import java.util.spi.ToolProvider; 47 48 /** 49 * Simple extraction tool which generates a minimal Java API. Such an API consists mainly of static methods, 50 * where for each native function a static method is added which calls the underlying native method handles. 51 * Similarly, for struct fields and global variables, static accessors (getter and setter) are generated 52 * on top of the underlying memory access var handles. For each struct, a static layout field is generated. 53 */ 54 public class Main { 55 private static final String MESSAGES_RESOURCE = "jdk.incubator.jextract.tool.resources.Messages"; 56 57 private static final ResourceBundle MESSAGES_BUNDLE; 58 static { 59 MESSAGES_BUNDLE = ResourceBundle.getBundle(MESSAGES_RESOURCE, Locale.getDefault()); 60 } 61 62 public static final boolean DEBUG = Boolean.getBoolean("jextract.debug"); 63 64 // error codes 65 private static final int SUCCESS = 0; 66 private static final int OPTION_ERROR = 1; 67 private static final int INPUT_ERROR = 2; 68 private static final int OUTPUT_ERROR = 3; 69 private static final int RUNTIME_ERROR = 4; 70 71 private final PrintWriter out; 72 private final PrintWriter err; 73 74 private static String format(String msgId, Object... args) { 75 return new MessageFormat(MESSAGES_BUNDLE.getString(msgId)).format(args); 76 } 77 78 private Main(PrintWriter out, PrintWriter err) { 79 this.out = out; 80 this.err = err; 81 } 82 83 private int printHelp(OptionParser parser, int exitCode) { 84 try { 85 parser.printHelpOn(err); 86 } catch (IOException ignored) {} 87 return exitCode; 88 } 89 90 public static void main(String[] args) { 91 if (args.length == 0) { 92 System.err.println("Expected a header file"); 93 return; 94 } 95 96 Main m = new Main(new PrintWriter(System.out, true), new PrintWriter(System.err, true)); 97 System.exit(m.run(args)); 98 } 99 100 private int run(String[] args) { 101 OptionParser parser = new OptionParser(false); 102 parser.accepts("C", format("help.C")).withRequiredArg(); 103 parser.accepts("I", format("help.I")).withRequiredArg(); 104 parser.acceptsAll(List.of("L", "library-path"), format("help.L")).withRequiredArg(); 105 parser.accepts("compile", format("help.compile")); 106 parser.accepts("filter", format("help.filter")).withRequiredArg(); 107 parser.accepts("l", format("help.l")).withRequiredArg(); 108 parser.accepts("output", format("help.output")).withRequiredArg(); 109 parser.acceptsAll(List.of("t", "target-package"), format("help.t")).withRequiredArg(); 110 parser.acceptsAll(List.of("?", "h", "help"), format("help.h")).forHelp(); 111 parser.nonOptions(format("help.non.option")); 112 113 OptionSet optionSet; 114 try { 115 optionSet = parser.parse(args); 116 } catch (OptionException oe) { 117 return printHelp(parser, OPTION_ERROR); 118 } 119 120 if (optionSet.has("h")) { 121 return printHelp(parser, SUCCESS); 122 } 123 124 if (optionSet.nonOptionArguments().size() != 1) { 125 return printHelp(parser, OPTION_ERROR); 126 } 127 128 Options.Builder builder = Options.builder(); 129 if (optionSet.has("I")) { 130 optionSet.valuesOf("I").forEach(p -> builder.addClangArg("-I" + p)); 131 } 132 133 Path builtinInc = Paths.get(System.getProperty("java.home"), "conf", "jextract"); 134 builder.addClangArg("-I" + builtinInc); 135 136 if (optionSet.has("C")) { 137 optionSet.valuesOf("C").forEach(p -> builder.addClangArg((String) p)); 138 } 139 140 if (optionSet.has("filter")) { 141 optionSet.valuesOf("filter").forEach(p -> builder.addFilter((String) p)); 142 } 143 144 if (optionSet.has("output")) { 145 builder.setOutputDir(optionSet.valueOf("output").toString()); 146 } 147 148 if (optionSet.has("compile")) { 149 builder.setCompile(); 150 } 151 152 boolean librariesSpecified = optionSet.has("l"); 153 if (librariesSpecified) { 154 for (Object arg : optionSet.valuesOf("l")) { 155 String lib = (String)arg; 156 if (lib.indexOf(File.separatorChar) != -1) { 157 err.println(format("l.name.should.not.be.path", lib)); 158 return OPTION_ERROR; 159 } 160 builder.addLibraryName(lib); 161 } 162 } 163 164 if (optionSet.has("L")) { 165 List<?> libpaths = optionSet.valuesOf("L"); 166 if (librariesSpecified) { 167 libpaths.forEach(p -> builder.addLibraryPath((String) p)); 168 } else { 169 // "L" with no "l" option! 170 err.println(format("warn.L.without.l")); 171 } 172 } 173 174 String targetPackage = optionSet.has("t") ? (String) optionSet.valueOf("t") : ""; 175 builder.setTargetPackage(targetPackage); 176 177 Options options = builder.build(); 178 179 Path header = Paths.get(optionSet.nonOptionArguments().get(0).toString()); 180 if (!Files.isReadable(header)) { 181 err.println(format("cannot.read.header.file", header)); 182 return INPUT_ERROR; 183 } 184 185 //parse 186 JextractTask jextractTask = JextractTask.newTask(options.compile, header); 187 Declaration.Scoped toplevel = jextractTask.parse(options.clangArgs.toArray(new String[0])); 188 189 //filter 190 if (!options.filters.isEmpty()) { 191 toplevel = Filter.filter(toplevel, options.filters.toArray(new String[0])); 192 } 193 194 //handle names 195 GroupNameHandler nameHandler = new GroupNameHandler(); 196 toplevel = nameHandler.fillNames(toplevel); 197 198 if (Main.DEBUG) { 199 System.out.println(toplevel); 200 } 201 202 Path output = Path.of(options.outputDir); 203 //generate 204 try { 205 JavaFileObject[] files = HandleSourceFactory.generateWrapped( 206 toplevel, 207 header.getFileName().toString().replace(".h", "_h"), 208 options.targetPackage, 209 options.libraryNames, 210 options.libraryPaths); 211 jextractTask.write(output, files); 212 } catch (RuntimeException re) { 213 err.println(re); 214 if (Main.DEBUG) { 215 re.printStackTrace(err); 216 } 217 return RUNTIME_ERROR; 218 } 219 return SUCCESS; 220 } 221 222 public static class JextractToolProvider implements ToolProvider { 223 @Override 224 public String name() { 225 return "jextract"; 226 } 227 228 @Override 229 public int run(PrintWriter out, PrintWriter err, String... args) { 230 // defensive check to throw security exception early. 231 // Note that the successful run of jextract under security 232 // manager would require far more permissions like loading 233 // library (clang), file system access etc. 234 if (System.getSecurityManager() != null) { 235 System.getSecurityManager(). 236 checkPermission(new RuntimePermission("jextract")); 237 } 238 239 Main instance = new Main(out, err); 240 return instance.run(args); 241 } 242 } 243 }