< prev index next >
src/jdk.jextract/share/classes/com/sun/tools/jextract/Context.java
Print this page
*** 20,581 ****
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jextract;
- import jdk.internal.clang.*;
-
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.IOException;
- import java.io.OutputStream;
import java.io.PrintWriter;
- import java.io.UncheckedIOException;
- import java.lang.invoke.MethodHandles;
- import java.lang.invoke.MethodHandles.Lookup;
- import java.foreign.Library;
- import java.foreign.Libraries;
import java.nio.file.Files;
import java.nio.file.Path;
! import java.nio.file.Paths;
! import java.util.ArrayList;
! import java.util.Arrays;
! import java.util.Collections;
! import java.util.HashMap;
! import java.util.List;
! import java.util.Map;
! import java.util.Optional;
! import java.util.Properties;
! import java.util.Set;
! import java.util.TreeSet;
! import java.util.function.Function;
! import java.util.function.Predicate;
! import java.util.jar.JarOutputStream;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
- import java.util.zip.ZipEntry;
import com.sun.tools.jextract.parser.Parser;
- import com.sun.tools.jextract.tree.FunctionTree;
- import com.sun.tools.jextract.tree.HeaderTree;
import com.sun.tools.jextract.tree.Tree;
- import static java.nio.file.StandardOpenOption.CREATE;
- import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
- import static java.nio.file.StandardOpenOption.WRITE;
-
/**
* The setup for the tool execution
*/
public final class Context {
- // The folder path mapping to package name
- private final Map<Path, String> pkgMap;
- // The header file parsed
- private final Map<Path, HeaderFile> headerMap;
// The args for parsing C
! private final List<String> clangArgs;
// The set of source header files
! private final Set<Path> sources;
// The list of library names
! private final List<String> libraryNames;
// The list of library paths
! private final List<String> libraryPaths;
// The list of library paths for link checks
! private final List<String> linkCheckPaths;
// Symbol patterns to be included
! private final List<Pattern> includeSymbols;
// Symbol patterns to be excluded
! private final List<Pattern> excludeSymbols;
// no NativeLocation info
! private boolean noNativeLocations;
// generate static forwarder class or not?
! private boolean genStaticForwarder;
!
! final PrintWriter out;
! final PrintWriter err;
! private Predicate<String> symChecker;
! private Predicate<String> includeSymFilter;
! private Predicate<String> excludeSymFilter;
- private final Parser parser;
-
- final static String defaultPkg = "jextract.dump";
final Logger logger = Logger.getLogger(getClass().getPackage().getName());
public Context(PrintWriter out, PrintWriter err) {
- this.pkgMap = new HashMap<>();
- this.headerMap = new HashMap<>();
this.clangArgs = new ArrayList<>();
this.sources = new TreeSet<>();
this.libraryNames = new ArrayList<>();
this.libraryPaths = new ArrayList<>();
this.linkCheckPaths = new ArrayList<>();
this.includeSymbols = new ArrayList<>();
this.excludeSymbols = new ArrayList<>();
- this.parser = new Parser(out, err, Main.INCLUDE_MACROS);
this.out = out;
this.err = err;
}
public Context() {
this(new PrintWriter(System.out, true), new PrintWriter(System.err, true));
}
! void addClangArg(String arg) {
clangArgs.add(arg);
}
public void addSource(Path path) {
sources.add(path);
}
! void addLibraryName(String name) {
libraryNames.add(name);
}
! void addLibraryPath(String path) {
libraryPaths.add(path);
}
! void addLinkCheckPath(String path) {
linkCheckPaths.add(path);
}
! boolean getNoNativeLocations() {
! return noNativeLocations;
! }
!
! void setNoNativeLocations() {
noNativeLocations = true;
}
! void addIncludeSymbols(String pattern) {
includeSymbols.add(Pattern.compile(pattern));
}
! void addExcludeSymbols(String pattern) {
excludeSymbols.add(Pattern.compile(pattern));
}
! void setGenStaticForwarder(boolean flag) {
this.genStaticForwarder = flag;
}
! // return the absolute path of the library of given name by searching
! // in the given array of paths.
! private static Optional<Path> findLibraryPath(Path[] paths, String libName) {
! return Arrays.stream(paths).
! map(p -> p.resolve(System.mapLibraryName(libName))).
! filter(Files::isRegularFile).map(Path::toAbsolutePath).findFirst();
! }
!
! /*
! * Load the specified shared libraries from the specified paths.
! *
! * @param lookup Lookup object of the caller.
! * @param pathStrs array of paths to load the shared libraries from.
! * @param names array of shared library names.
! */
! // used by jextract tool to load libraries for symbol checks.
! public static Library[] loadLibraries(Lookup lookup, String[] pathStrs, String[] names) {
! if (pathStrs == null || pathStrs.length == 0) {
! return Arrays.stream(names).map(
! name -> Libraries.loadLibrary(lookup, name)).toArray(Library[]::new);
! } else {
! Path[] paths = Arrays.stream(pathStrs).map(Paths::get).toArray(Path[]::new);
! return Arrays.stream(names).map(libName -> {
! Optional<Path> absPath = findLibraryPath(paths, libName);
! return absPath.isPresent() ?
! Libraries.load(lookup, absPath.get().toString()) :
! Libraries.loadLibrary(lookup, libName);
! }).toArray(Library[]::new);
! }
! }
!
! private void initSymChecker() {
! if (!libraryNames.isEmpty() && !linkCheckPaths.isEmpty()) {
! try {
! Library[] libs = loadLibraries(MethodHandles.lookup(),
! linkCheckPaths.toArray(new String[0]),
! libraryNames.toArray(new String[0]));
! // check if the given symbol is found in any of the libraries or not.
! // If not found, warn the user for the missing symbol.
! symChecker = name -> {
! if (Main.DEBUG) {
! err.println("Searching symbol: " + name);
! }
! return (Arrays.stream(libs).filter(lib -> {
! try {
! lib.lookup(name);
! if (Main.DEBUG) {
! err.println("Found symbol: " + name);
! }
! return true;
! } catch (NoSuchMethodException nsme) {
! return false;
! }
! }).findFirst().isPresent());
! };
! } catch (UnsatisfiedLinkError ex) {
! err.println(Main.format("warn.lib.not.found"));
! symChecker = null;
! }
! } else {
! symChecker = null;
! }
! }
!
! private boolean isSymbolFound(String name) {
! return symChecker == null? true : symChecker.test(name);
! }
!
! private void initSymFilters() {
! if (!includeSymbols.isEmpty()) {
! Pattern[] pats = includeSymbols.toArray(new Pattern[0]);
! includeSymFilter = name -> {
! return Arrays.stream(pats).filter(pat -> pat.matcher(name).matches()).
! findFirst().isPresent();
! };
! } else {
! includeSymFilter = null;
! }
!
! if (!excludeSymbols.isEmpty()) {
! Pattern[] pats = excludeSymbols.toArray(new Pattern[0]);
! excludeSymFilter = name -> {
! return Arrays.stream(pats).filter(pat -> pat.matcher(name).matches()).
! findFirst().isPresent();
! };
! } else {
! excludeSymFilter = null;
! }
! }
!
! private boolean isSymbolIncluded(String name) {
! return includeSymFilter == null? true : includeSymFilter.test(name);
! }
!
! private boolean isSymbolExcluded(String name) {
! return excludeSymFilter == null? false : excludeSymFilter.test(name);
! }
!
! /**
! * Setup a package name for a given folder.
! *
! * @param folder The path to the folder, use null to set catch-all.
! * @param pkg The package name
! * @return True if the folder is setup successfully. False is a package
! * has been assigned for the folder.
! */
! public boolean usePackageForFolder(Path folder, String pkg) {
! if (folder != null) {
! folder = folder.toAbsolutePath();
! if (!Files.isDirectory(folder)) {
! folder = folder.getParent();
! }
! }
! String existing = pkgMap.putIfAbsent(folder, pkg);
! final String finalFolder = (null == folder) ? "all folders not configured" : folder.toString();
! if (null == existing) {
! logger.config(() -> "Package " + pkg + " is selected for " + finalFolder);
! return true;
! } else {
! logger.warning(() -> "Package " + existing + " had been selected for " + finalFolder + ", request to use " + pkg + " is ignored.");
! return false;
! }
! }
!
! static class Entity {
! final String pkg;
! final String entity;
!
! Entity(String pkg, String entity) {
! this.pkg = pkg;
! this.entity = entity;
! }
! }
!
! /**
! * Determine package and interface name given a path. If the path is
! * a folder, then only package name is determined. The package name is
! * determined with the longest path matching the setup. If the path is not
! * setup for any package, the default package name is returned.
! *
! * @param origin The source path
! * @return The Entity
! * @see Context::usePackageForFolder(Path, String)
! */
! Entity whatis(Path origin) {
! // normalize to absolute path
! origin = origin.toAbsolutePath();
! String filename = null;
! if (!Files.isDirectory(origin)) {
! // ensure it's a folder name
! filename = origin.getFileName().toString();
! origin = origin.getParent();
! }
! Path path = origin;
!
! // search the map for a hit with longest path
! while (path != null && !pkgMap.containsKey(path)) {
! path = path.getParent();
! }
!
! int start;
! String pkg;
! if (path != null) {
! start = path.getNameCount();
! pkg = pkgMap.get(path);
! } else {
! pkg = pkgMap.get(null);
! if (pkg == null) {
! start = 0;
! pkg = defaultPkg;
! } else {
! start = origin.getNameCount();
! }
! }
!
! if (filename == null) {
! // a folder, only pkg name matters
! return new Entity(pkg, null);
! }
!
! StringBuilder sb = new StringBuilder();
! while (start < origin.getNameCount()) {
! sb.append(Utils.toJavaIdentifier(origin.getName(start++).toString()));
! sb.append("_");
! }
!
! int ext = filename.lastIndexOf('.');
! if (ext != -1) {
! sb.append(filename.substring(0, ext));
! } else {
! sb.append(filename);
! }
! return new Entity(pkg, Utils.toClassName(sb.toString()));
! }
!
! HeaderFile getHeaderFile(Path header, HeaderFile main) {
! if (!Files.isRegularFile(header)) {
! logger.warning(() -> "Not a regular file: " + header.toString());
! throw new IllegalArgumentException(header.toString());
! }
!
! final Context.Entity e = whatis(header);
! HeaderFile headerFile = new HeaderFile(this, header, e.pkg, e.entity, main);
! headerFile.useLibraries(libraryNames, libraryPaths);
! return headerFile;
! }
!
! void processTree(Tree tree, HeaderFile main, Function<HeaderFile, AsmCodeFactory> fn) {
! SourceLocation loc = tree.location();
!
! HeaderFile header;
! boolean isBuiltIn = false;
!
! if (tree.isFromMain()) {
! header = main;
! } else {
! SourceLocation.Location src = loc.getFileLocation();
! if (src == null) {
! logger.info(() -> "Tree " + tree.name() + "@" + tree.USR() + " has no FileLocation");
! return;
! }
!
! Path p = src.path();
! if (p == null) {
! logger.fine(() -> "Found built-in type: " + tree.name());
! header = main;
! isBuiltIn = true;
! } else {
! p = p.normalize().toAbsolutePath();
! header = headerMap.get(p);
! if (header == null) {
! final HeaderFile hf = header = getHeaderFile(p, main);
! logger.config(() -> "First encounter of header file " + hf.path + ", assigned to package " + hf.pkgName);
! // Only generate code for header files specified or in the same package
! if (sources.contains(p) ||
! (header.pkgName.equals(main.pkgName))) {
! logger.config("Code gen for header " + p + " enabled in package " + header.pkgName);
! header.useCodeFactory(fn.apply(header));
! }
! headerMap.put(p, header);
! }
! }
! }
!
! header.processTree(tree, main, isBuiltIn);
}
! public void parse() {
! parse(header -> genStaticForwarder?
! new AsmCodeFactoryExt(this, header) : new AsmCodeFactory(this, header));
! }
!
! private boolean symbolFilter(Tree tree) {
! String name = tree.name();
! if (!isSymbolIncluded(name) || isSymbolExcluded(name)) {
! return false;
! }
!
! // check for function symbols in libraries & warn missing symbols
! if (tree instanceof FunctionTree && !isSymbolFound(name)) {
! err.println(Main.format("warn.symbol.not.found", name));
! //auto-exclude symbols not found
! return false;
! }
!
! return true;
! }
!
! public HeaderFile headerFor(Path p) {
! return headerMap.get(p);
! }
!
! public void parse(Function<HeaderFile, AsmCodeFactory> fn) {
! initSymChecker();
! initSymFilters();
!
! List<HeaderTree> headers = parser.parse(sources, clangArgs);
! processHeaders(headers, fn);
! }
!
! private void processHeaders(List<HeaderTree> headers, Function<HeaderFile, AsmCodeFactory> fn) {
! headers.stream().
! map(new TreeFilter(this::symbolFilter)).
! map(new TypedefHandler()).
! map(new EmptyNameHandler()).
! forEach(header -> {
! HeaderFile hf = headerMap.computeIfAbsent(header.path(), p -> getHeaderFile(p, null));
! hf.useCodeFactory(fn.apply(hf));
! logger.info(() -> "Processing header file " + header.path());
! header.declarations().stream()
! .peek(decl -> logger.finest(
! () -> "Cursor: " + decl.name() + "@" + decl.USR() + "?" + decl.isDeclaration()))
! .forEach(decl -> processTree(decl, hf, fn));
! });
! }
!
! private Map<String, List<AsmCodeFactory>> getPkgCfMap() {
! final Map<String, List<AsmCodeFactory>> mapPkgCf = new HashMap<>();
! // Build the pkg to CodeFactory map
! headerMap.values().forEach(header -> {
! AsmCodeFactory cf = header.getCodeFactory();
! String pkg = header.pkgName;
! logger.config(() -> "File " + header + " is in package: " + pkg);
! if (cf == null) {
! logger.config(() -> "File " + header + " code generation is not activated!");
! return;
! }
! List<AsmCodeFactory> l = mapPkgCf.computeIfAbsent(pkg, k -> new ArrayList<>());
! l.add(cf);
! logger.config(() -> "Add cf " + cf + " to pkg " + pkg + ", size is now " + l.size());
! });
! return Collections.unmodifiableMap(mapPkgCf);
! }
!
! public Map<String, byte[]> collectClasses(String... pkgs) {
! final Map<String, byte[]> rv = new HashMap<>();
! final Map<String, List<AsmCodeFactory>> mapPkgCf = getPkgCfMap();
! for (String pkg_name : pkgs) {
! mapPkgCf.getOrDefault(pkg_name, Collections.emptyList())
! .forEach(cf -> rv.putAll(cf.collect()));
! }
! return Collections.unmodifiableMap(rv);
! }
!
! private static final String JEXTRACT_MANIFEST = "META-INFO" + File.separatorChar + "jextract.properties";
!
! @SuppressWarnings("deprecation")
! private byte[] getJextractProperties(String[] args) {
! Properties props = new Properties();
! props.setProperty("os.name", System.getProperty("os.name"));
! props.setProperty("os.version", System.getProperty("os.version"));
! props.setProperty("os.arch", System.getProperty("os.arch"));
! props.setProperty("jextract.args", Arrays.toString(args));
! ByteArrayOutputStream baos = new ByteArrayOutputStream();
! props.save(baos, "jextract meta data");
! return baos.toByteArray();
! }
!
! void collectClassFiles(Path destDir, String[] args, String... pkgs) throws IOException {
! try {
! collectClasses(pkgs).entrySet().stream().forEach(e -> {
! try {
! String path = e.getKey().replace('.', File.separatorChar) + ".class";
! logger.fine(() -> "Writing " + path);
! Path fullPath = destDir.resolve(path).normalize();
! Files.createDirectories(fullPath.getParent());
! try (OutputStream fos = Files.newOutputStream(fullPath)) {
! fos.write(e.getValue());
! fos.flush();
! }
! } catch (IOException ioe) {
! throw new UncheckedIOException(ioe);
! }
! });
!
! Path propsPath = destDir.resolve(JEXTRACT_MANIFEST).normalize();
! Files.createDirectories(propsPath.getParent());
! try (OutputStream fos = Files.newOutputStream(propsPath)) {
! fos.write(getJextractProperties(args));
! fos.flush();
! }
! } catch (UncheckedIOException uioe) {
! throw uioe.getCause();
! }
! }
!
! private void writeJar(AsmCodeFactory cf, JarOutputStream jar) {
! cf.collect().entrySet().stream().forEach(e -> {
! try {
! String path = e.getKey().replace('.', File.separatorChar) + ".class";
! logger.fine(() -> "Add " + path);
! jar.putNextEntry(new ZipEntry(path));
! jar.write(e.getValue());
! jar.closeEntry();
! } catch (IOException ioe) {
! throw new UncheckedIOException(ioe);
! }
! });
! }
!
! public void collectJarFile(final JarOutputStream jos, String[] args, String... pkgs) {
! final Map<String, List<AsmCodeFactory>> mapPkgCf = getPkgCfMap();
!
! for (String pkg_name : pkgs) {
! // convert '.' to '/' to use as a path
! String entryName = Utils.toInternalName(pkg_name, "");
! // package folder
! if (!entryName.isEmpty()) {
! try {
! jos.putNextEntry(new ZipEntry(entryName));
! } catch (IOException ex) {
! throw new UncheckedIOException(ex);
! }
! }
! logger.fine(() -> "Produce for package " + pkg_name);
! mapPkgCf.getOrDefault(pkg_name, Collections.emptyList())
! .forEach(cf -> writeJar(cf, jos));
! }
!
! try {
! jos.putNextEntry(new ZipEntry(JEXTRACT_MANIFEST));
! jos.write(getJextractProperties(args));
! jos.closeEntry();
! } catch (IOException ioe) {
! throw new UncheckedIOException(ioe);
! }
! }
!
! void collectJarFile(final Path jar, String[] args, String... pkgs) throws IOException {
! logger.info(() -> "Collecting jar file " + jar);
! try (OutputStream os = Files.newOutputStream(jar, CREATE, TRUNCATE_EXISTING, WRITE);
! JarOutputStream jo = new JarOutputStream(os)) {
! collectJarFile(jo, args, pkgs);
! } catch (UncheckedIOException uioe) {
! throw uioe.getCause();
! }
}
}
--- 20,126 ----
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jextract;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
! import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.sun.tools.jextract.parser.Parser;
import com.sun.tools.jextract.tree.Tree;
/**
* The setup for the tool execution
*/
public final class Context {
// The args for parsing C
! public final List<String> clangArgs;
// The set of source header files
! public final Set<Path> sources;
// The list of library names
! public final List<String> libraryNames;
// The list of library paths
! public final List<String> libraryPaths;
// The list of library paths for link checks
! public final List<String> linkCheckPaths;
// Symbol patterns to be included
! public final List<Pattern> includeSymbols;
// Symbol patterns to be excluded
! public final List<Pattern> excludeSymbols;
// no NativeLocation info
! public boolean noNativeLocations;
// generate static forwarder class or not?
! public boolean genStaticForwarder;
! // target package
! public String targetPackage;
! // package mappings
! public final Map<Path, String> pkgMappings = new LinkedHashMap<>();
! public final PrintWriter out;
! public final PrintWriter err;
final Logger logger = Logger.getLogger(getClass().getPackage().getName());
public Context(PrintWriter out, PrintWriter err) {
this.clangArgs = new ArrayList<>();
this.sources = new TreeSet<>();
this.libraryNames = new ArrayList<>();
this.libraryPaths = new ArrayList<>();
this.linkCheckPaths = new ArrayList<>();
this.includeSymbols = new ArrayList<>();
this.excludeSymbols = new ArrayList<>();
this.out = out;
this.err = err;
}
public Context() {
this(new PrintWriter(System.out, true), new PrintWriter(System.err, true));
}
! public void addClangArg(String arg) {
clangArgs.add(arg);
}
public void addSource(Path path) {
sources.add(path);
}
! public void addLibraryName(String name) {
libraryNames.add(name);
}
! public void addLibraryPath(String path) {
libraryPaths.add(path);
}
! public void addLinkCheckPath(String path) {
linkCheckPaths.add(path);
}
! public void setNoNativeLocations() {
noNativeLocations = true;
}
! public void addIncludeSymbols(String pattern) {
includeSymbols.add(Pattern.compile(pattern));
}
! public void addExcludeSymbols(String pattern) {
excludeSymbols.add(Pattern.compile(pattern));
}
! public void setGenStaticForwarder(boolean flag) {
this.genStaticForwarder = flag;
}
! public void setTargetPackage(String pkg) {
! this.targetPackage = pkg;
}
! public void addPackageMapping(Path path, String pkg) {
! pkgMappings.put(path, pkg);
}
}
< prev index next >