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.clang.*; 26 import jdk.internal.foreign.LibrariesHelper; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.OutputStream; 31 import java.io.PrintWriter; 32 import java.io.UncheckedIOException; 33 import java.lang.invoke.MethodHandles; 34 import java.foreign.Library; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.HashMap; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.TreeSet; 45 import java.util.function.Function; 46 import java.util.function.Predicate; 47 import java.util.jar.JarOutputStream; 48 import java.util.logging.Logger; 49 import java.util.regex.Pattern; 50 import java.util.stream.Collectors; 51 import java.util.zip.ZipEntry; 52 53 import static java.nio.file.StandardOpenOption.CREATE; 54 import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; 55 import static java.nio.file.StandardOpenOption.WRITE; 56 57 /** 58 * The setup for the tool execution 59 */ 60 public final class Context { 61 // package name to TypeDictionary 62 private final Map<String, TypeDictionary> tdMap; 63 // The folder path mapping to package name 64 private final Map<Path, String> pkgMap; 65 // The header file parsed 66 private final Map<Path, HeaderFile> headerMap; 67 // The args for parsing C 68 private final List<String> clangArgs; 69 // The set of source header files 70 private final Set<Path> sources; 71 // The list of library names 72 private final List<String> libraryNames; 73 // The list of library paths 74 private final List<String> libraryPaths; 75 // The list of library paths for link checks 76 private final List<String> linkCheckPaths; 77 // Symbol patterns to be excluded 78 private final List<Pattern> excludeSymbols; 79 80 final PrintWriter out; 81 final PrintWriter err; 82 83 private Predicate<String> symChecker; 84 private Predicate<String> symFilter; 85 86 private final MacroParser macroParser; 87 88 private final static String defaultPkg = "jextract.dump"; 89 final Logger logger = Logger.getLogger(getClass().getPackage().getName()); 90 91 public Context(PrintWriter out, PrintWriter err) { 92 this.tdMap = new HashMap<>(); 93 this.pkgMap = new HashMap<>(); 94 this.headerMap = new HashMap<>(); 95 this.clangArgs = new ArrayList<>(); 96 this.sources = new TreeSet<>(); 97 this.libraryNames = new ArrayList<>(); 98 this.libraryPaths = new ArrayList<>(); 99 this.linkCheckPaths = new ArrayList<>(); 100 this.excludeSymbols = new ArrayList<>(); 101 this.macroParser = new MacroParser(); 102 this.out = out; 103 this.err = err; 104 } 105 106 public Context() { 107 this(new PrintWriter(System.out, true), new PrintWriter(System.err, true)); 108 } 109 110 TypeDictionary typeDictionaryFor(String pkg) { 111 return tdMap.computeIfAbsent(pkg, p->new TypeDictionary(this, p)); 112 } 113 114 void addClangArg(String arg) { 115 clangArgs.add(arg); 116 } 117 118 public void addSource(Path path) { 119 sources.add(path); 120 } 121 122 void addLibraryName(String name) { 123 libraryNames.add(name); 124 } 125 126 void addLibraryPath(String path) { 127 libraryPaths.add(path); 419 logger.config(() -> "File " + header + " code generation is not activated!"); 420 return; 421 } 422 List<AsmCodeFactory> l = mapPkgCf.computeIfAbsent(pkg, k -> new ArrayList<>()); 423 l.add(cf); 424 logger.config(() -> "Add cf " + cf + " to pkg " + pkg + ", size is now " + l.size()); 425 }); 426 return Collections.unmodifiableMap(mapPkgCf); 427 } 428 429 public Map<String, byte[]> collectClasses(String... pkgs) { 430 final Map<String, byte[]> rv = new HashMap<>(); 431 final Map<String, List<AsmCodeFactory>> mapPkgCf = getPkgCfMap(); 432 for (String pkg_name : pkgs) { 433 mapPkgCf.getOrDefault(pkg_name, Collections.emptyList()) 434 .forEach(cf -> rv.putAll(cf.collect())); 435 } 436 return Collections.unmodifiableMap(rv); 437 } 438 439 void collectClassFiles(Path destDir, String... pkgs) throws IOException { 440 try { 441 collectClasses(pkgs).entrySet().stream().forEach(e -> { 442 try { 443 String path = e.getKey().replace('.', File.separatorChar) + ".class"; 444 logger.fine(() -> "Writing " + path); 445 Path fullPath = destDir.resolve(path).normalize(); 446 Files.createDirectories(fullPath.getParent()); 447 try (OutputStream fos = Files.newOutputStream(fullPath)) { 448 fos.write(e.getValue()); 449 fos.flush(); 450 } 451 } catch (IOException ioe) { 452 throw new UncheckedIOException(ioe); 453 } 454 }); 455 } catch (UncheckedIOException uioe) { 456 throw uioe.getCause(); 457 } 458 } 459 460 private void writeJar(AsmCodeFactory cf, JarOutputStream jar) { 461 cf.collect().entrySet().stream().forEach(e -> { 462 try { 463 String path = e.getKey().replace('.', File.separatorChar) + ".class"; 464 logger.fine(() -> "Add " + path); 465 jar.putNextEntry(new ZipEntry(path)); 466 jar.write(e.getValue()); 467 jar.closeEntry(); 468 } catch (IOException ioe) { 469 throw new UncheckedIOException(ioe); 470 } 471 }); 472 } 473 474 public void collectJarFile(final JarOutputStream jos, String... pkgs) { 479 String entryName = Utils.toInternalName(pkg_name, ""); 480 // package folder 481 if (!entryName.isEmpty()) { 482 try { 483 jos.putNextEntry(new ZipEntry(entryName)); 484 } catch (IOException ex) { 485 throw new UncheckedIOException(ex); 486 } 487 } 488 logger.fine(() -> "Produce for package " + pkg_name); 489 mapPkgCf.getOrDefault(pkg_name, Collections.emptyList()) 490 .forEach(cf -> writeJar(cf, jos)); 491 } 492 } 493 494 void collectJarFile(final Path jar, String... pkgs) throws IOException { 495 logger.info(() -> "Collecting jar file " + jar); 496 try (OutputStream os = Files.newOutputStream(jar, CREATE, TRUNCATE_EXISTING, WRITE); 497 JarOutputStream jo = new JarOutputStream(os)) { 498 collectJarFile(jo, pkgs); 499 } catch (UncheckedIOException uioe) { 500 throw uioe.getCause(); 501 } 502 } 503 504 /** 505 * Perform a local lookup, any undefined type will cause a JType 506 * be defined within origin scope. 507 * 508 * @param type The libclang type 509 * @param origin The path of the file where type is encountered 510 * @return The JType 511 */ 512 JType getJType(final Type type, Path origin) { 513 Path p = origin.normalize().toAbsolutePath(); 514 515 HeaderFile hf = headerMap.get(p); 516 // We should not encounter a type if the header file reference to it is not yet processed 517 assert(null != hf); 518 if (hf == null) { | 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.clang.*; 26 import jdk.internal.foreign.LibrariesHelper; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.File; 30 import java.io.IOException; 31 import java.io.OutputStream; 32 import java.io.PrintWriter; 33 import java.io.UncheckedIOException; 34 import java.lang.invoke.MethodHandles; 35 import java.foreign.Library; 36 import java.nio.file.Files; 37 import java.nio.file.Path; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collections; 41 import java.util.HashMap; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Properties; 45 import java.util.Set; 46 import java.util.TreeSet; 47 import java.util.function.Function; 48 import java.util.function.Predicate; 49 import java.util.jar.JarOutputStream; 50 import java.util.logging.Logger; 51 import java.util.regex.Pattern; 52 import java.util.stream.Collectors; 53 import java.util.zip.ZipEntry; 54 55 import static java.nio.file.StandardOpenOption.CREATE; 56 import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; 57 import static java.nio.file.StandardOpenOption.WRITE; 58 59 /** 60 * The setup for the tool execution 61 */ 62 public final class Context { 63 // package name to TypeDictionary 64 private final Map<String, TypeDictionary> tdMap; 65 // The folder path mapping to package name 66 private final Map<Path, String> pkgMap; 67 // The header file parsed 68 private final Map<Path, HeaderFile> headerMap; 69 // The args for parsing C 70 private final List<String> clangArgs; 71 // The set of source header files 72 private final Set<Path> sources; 73 // The list of library names 74 private final List<String> libraryNames; 75 // The list of library paths 76 private final List<String> libraryPaths; 77 // The list of library paths for link checks 78 private final List<String> linkCheckPaths; 79 // Symbol patterns to be excluded 80 private final List<Pattern> excludeSymbols; 81 82 final PrintWriter out; 83 final PrintWriter err; 84 final String[] args; 85 86 private Predicate<String> symChecker; 87 private Predicate<String> symFilter; 88 89 private final MacroParser macroParser; 90 91 private final static String defaultPkg = "jextract.dump"; 92 final Logger logger = Logger.getLogger(getClass().getPackage().getName()); 93 94 public Context(PrintWriter out, PrintWriter err, String[] args) { 95 this.tdMap = new HashMap<>(); 96 this.pkgMap = new HashMap<>(); 97 this.headerMap = new HashMap<>(); 98 this.clangArgs = new ArrayList<>(); 99 this.sources = new TreeSet<>(); 100 this.libraryNames = new ArrayList<>(); 101 this.libraryPaths = new ArrayList<>(); 102 this.linkCheckPaths = new ArrayList<>(); 103 this.excludeSymbols = new ArrayList<>(); 104 this.macroParser = new MacroParser(); 105 this.out = out; 106 this.err = err; 107 this.args = args; 108 } 109 110 public Context(String[] args) { 111 this(new PrintWriter(System.out, true), new PrintWriter(System.err, true), args); 112 } 113 114 TypeDictionary typeDictionaryFor(String pkg) { 115 return tdMap.computeIfAbsent(pkg, p->new TypeDictionary(this, p)); 116 } 117 118 void addClangArg(String arg) { 119 clangArgs.add(arg); 120 } 121 122 public void addSource(Path path) { 123 sources.add(path); 124 } 125 126 void addLibraryName(String name) { 127 libraryNames.add(name); 128 } 129 130 void addLibraryPath(String path) { 131 libraryPaths.add(path); 423 logger.config(() -> "File " + header + " code generation is not activated!"); 424 return; 425 } 426 List<AsmCodeFactory> l = mapPkgCf.computeIfAbsent(pkg, k -> new ArrayList<>()); 427 l.add(cf); 428 logger.config(() -> "Add cf " + cf + " to pkg " + pkg + ", size is now " + l.size()); 429 }); 430 return Collections.unmodifiableMap(mapPkgCf); 431 } 432 433 public Map<String, byte[]> collectClasses(String... pkgs) { 434 final Map<String, byte[]> rv = new HashMap<>(); 435 final Map<String, List<AsmCodeFactory>> mapPkgCf = getPkgCfMap(); 436 for (String pkg_name : pkgs) { 437 mapPkgCf.getOrDefault(pkg_name, Collections.emptyList()) 438 .forEach(cf -> rv.putAll(cf.collect())); 439 } 440 return Collections.unmodifiableMap(rv); 441 } 442 443 private static final String JEXTRACT_MANIFEST = "META-INFO" + File.separatorChar + "jextract.properties"; 444 445 @SuppressWarnings("deprecation") 446 private byte[] getJextractProperties() { 447 Properties props = new Properties(); 448 props.setProperty("os.name", System.getProperty("os.name")); 449 props.setProperty("os.version", System.getProperty("os.version")); 450 props.setProperty("os.arch", System.getProperty("os.arch")); 451 props.setProperty("jextract.args", Arrays.toString(args)); 452 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 453 props.save(baos, "jextract meta data"); 454 return baos.toByteArray(); 455 } 456 457 void collectClassFiles(Path destDir, String... pkgs) throws IOException { 458 try { 459 collectClasses(pkgs).entrySet().stream().forEach(e -> { 460 try { 461 String path = e.getKey().replace('.', File.separatorChar) + ".class"; 462 logger.fine(() -> "Writing " + path); 463 Path fullPath = destDir.resolve(path).normalize(); 464 Files.createDirectories(fullPath.getParent()); 465 try (OutputStream fos = Files.newOutputStream(fullPath)) { 466 fos.write(e.getValue()); 467 fos.flush(); 468 } 469 } catch (IOException ioe) { 470 throw new UncheckedIOException(ioe); 471 } 472 }); 473 474 Path propsPath = destDir.resolve(JEXTRACT_MANIFEST).normalize(); 475 Files.createDirectories(propsPath.getParent()); 476 try (OutputStream fos = Files.newOutputStream(propsPath)) { 477 fos.write(getJextractProperties()); 478 fos.flush(); 479 } 480 } catch (UncheckedIOException uioe) { 481 throw uioe.getCause(); 482 } 483 } 484 485 private void writeJar(AsmCodeFactory cf, JarOutputStream jar) { 486 cf.collect().entrySet().stream().forEach(e -> { 487 try { 488 String path = e.getKey().replace('.', File.separatorChar) + ".class"; 489 logger.fine(() -> "Add " + path); 490 jar.putNextEntry(new ZipEntry(path)); 491 jar.write(e.getValue()); 492 jar.closeEntry(); 493 } catch (IOException ioe) { 494 throw new UncheckedIOException(ioe); 495 } 496 }); 497 } 498 499 public void collectJarFile(final JarOutputStream jos, String... pkgs) { 504 String entryName = Utils.toInternalName(pkg_name, ""); 505 // package folder 506 if (!entryName.isEmpty()) { 507 try { 508 jos.putNextEntry(new ZipEntry(entryName)); 509 } catch (IOException ex) { 510 throw new UncheckedIOException(ex); 511 } 512 } 513 logger.fine(() -> "Produce for package " + pkg_name); 514 mapPkgCf.getOrDefault(pkg_name, Collections.emptyList()) 515 .forEach(cf -> writeJar(cf, jos)); 516 } 517 } 518 519 void collectJarFile(final Path jar, String... pkgs) throws IOException { 520 logger.info(() -> "Collecting jar file " + jar); 521 try (OutputStream os = Files.newOutputStream(jar, CREATE, TRUNCATE_EXISTING, WRITE); 522 JarOutputStream jo = new JarOutputStream(os)) { 523 collectJarFile(jo, pkgs); 524 jo.putNextEntry(new ZipEntry(JEXTRACT_MANIFEST)); 525 jo.write(getJextractProperties()); 526 jo.closeEntry(); 527 } catch (UncheckedIOException uioe) { 528 throw uioe.getCause(); 529 } 530 } 531 532 /** 533 * Perform a local lookup, any undefined type will cause a JType 534 * be defined within origin scope. 535 * 536 * @param type The libclang type 537 * @param origin The path of the file where type is encountered 538 * @return The JType 539 */ 540 JType getJType(final Type type, Path origin) { 541 Path p = origin.normalize().toAbsolutePath(); 542 543 HeaderFile hf = headerMap.get(p); 544 // We should not encounter a type if the header file reference to it is not yet processed 545 assert(null != hf); 546 if (hf == null) { |