Skip to content

Commit

Permalink
* Add Loader.getCanonicalPath() to work around bugs in `File.getCa…
Browse files Browse the repository at this point in the history
…nonicalPath()` on Windows (pull #519)
  • Loading branch information
devjeonghwan authored Oct 20, 2021
1 parent 5912c5c commit 28f89ad
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

* Add `Loader.getCanonicalPath()` to work around bugs in `File.getCanonicalPath()` on Windows ([pull #519](https://github.com/bytedeco/javacpp/pull/519))
* Add `FunctionPointer` and `@Virtual` methods missing from config files required by GraalVM Native Image
* Let `Tokenizer` convert characters using ASCII escape sequences `'\x...'` to normal hexadecimal values `0x...`
* Fix `Parser` incorrectly mapping default function arguments containing multiple template arguments
Expand Down
34 changes: 29 additions & 5 deletions src/main/java/org/bytedeco/javacpp/Loader.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public class Loader {
/** Value created out of "java.vm.name", "os.name", and "os.arch" system properties.
* Returned by {@link #getPlatform()} and initialized with {@link Detector#getPlatform()}. */
private static final String PLATFORM = Detector.getPlatform();
private static final boolean WINDOWS = PLATFORM.startsWith("windows");

/** Default platform properties loaded and returned by {@link #loadProperties()}. */
private static Properties platformProperties = null;
/** The stack of classes currently being loaded to support more than one class loader. */
Expand Down Expand Up @@ -137,6 +139,28 @@ public static String getPlatform() {
return PLATFORM;
}

/**
* Returns {@code file.getCanonicalPath()} or {@code file.toPath().toRealPath().toString()} on Windows.
* @return The canonical pathname string denoting the same file or directory as that abstract pathname.
* @throws IOException if an I/O error occurs
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-8003887">https://bugs.openjdk.java.net/browse/JDK-8003887</a>
*/
public static String getCanonicalPath(File file) throws IOException {
// When file does not exist, Path.toRealPath() throws IOException, but File.getCanonicalPath() does not
return WINDOWS && file.exists() ? file.toPath().toRealPath().toString() : file.getCanonicalPath();
}

/**
* Returns {@code file.getCanonicalFile()} or {@code file.toPath().toRealPath().toFile()} on Windows.
* @return The canonical file denoting the same file or directory as that abstract pathname.
* @throws IOException if an I/O error occurs
* @see <a href="https://bugs.openjdk.java.net/browse/JDK-8003887">https://bugs.openjdk.java.net/browse/JDK-8003887</a>
*/
public static File getCanonicalFile(File file) throws IOException {
// When file does not exist, Path.toRealPath() throws IOException, but File.getCanonicalFile() does not
return WINDOWS && file.exists() ? file.toPath().toRealPath().toFile() : file.getCanonicalFile();
}

/**
* Loads the {@link Properties} associated with the default {@link #getPlatform()}.
*
Expand Down Expand Up @@ -476,7 +500,7 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept
boolean reference = false;
long size = 0, timestamp = 0;
File cacheDir = getCacheDir();
File cacheSubdir = cacheDir.getCanonicalFile();
File cacheSubdir = Loader.getCanonicalFile(cacheDir);
String s = System.getProperty("org.bytedeco.javacpp.cachedir.nosubdir", "false").toLowerCase();
boolean noSubdir = s.equals("true") || s.equals("t") || s.equals("");
URLConnection urlConnection = resourceURL.openConnection();
Expand Down Expand Up @@ -637,7 +661,7 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept
}
// ... check if it has not already been extracted, and if not ...
if (!file.exists() || file.length() != size || file.lastModified() != timestamp
|| !cacheSubdir.equals(file.getCanonicalFile().getParentFile())) {
|| !cacheSubdir.equals(Loader.getCanonicalFile(file).getParentFile())) {
// ... add lock to avoid two JVMs access cacheDir simultaneously and ...
synchronized (Runtime.getRuntime()) {
try {
Expand All @@ -648,7 +672,7 @@ public static File cacheResource(URL resourceURL, String target) throws IOExcept
lock = lockChannel.lock();
// ... check if other JVM has extracted it before this JVM get the lock ...
if (!file.exists() || file.length() != size || file.lastModified() != timestamp
|| !cacheSubdir.equals(file.getCanonicalFile().getParentFile())) {
|| !cacheSubdir.equals(Loader.getCanonicalFile(file).getParentFile())) {
// ... extract it from our resources ...
if (logger.isDebugEnabled()) {
logger.debug("Extracting " + resourceURL);
Expand Down Expand Up @@ -766,7 +790,7 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
if (entry.isDirectory()) {
file.mkdirs();
} else if (!cacheDirectory || !file.exists() || file.length() != entrySize
|| file.lastModified() != entryTimestamp || !file.equals(file.getCanonicalFile())) {
|| file.lastModified() != entryTimestamp || !file.equals(Loader.getCanonicalFile(file))) {
// ... extract it from our resources ...
file.delete();
String s = resourceURL.toString();
Expand Down Expand Up @@ -1222,7 +1246,7 @@ public static String load(Class cls, Properties properties, boolean pathsFirst,

String cacheDir = null;
try {
cacheDir = getCacheDir().getCanonicalPath();
cacheDir = Loader.getCanonicalPath(getCacheDir());
} catch (IOException e) {
// no cache dir, no worries
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/bytedeco/javacpp/tools/BuildMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ static String[] merge(String[] ss, String s) {
}
properties.setProperty("platform.artifacts", project.getBuild().getOutputDirectory());
for (Artifact a : plugin.getArtifacts()) {
String s = a.getFile().getCanonicalPath();
String s = Loader.getCanonicalPath(a.getFile());
String v = properties.getProperty("platform.artifacts", "");
properties.setProperty("platform.artifacts",
v.length() == 0 || v.endsWith(separator) ? v + s : v + separator + s);
Expand Down
53 changes: 29 additions & 24 deletions src/main/java/org/bytedeco/javacpp/tools/Builder.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ void includeJavaPaths(ClassProperties properties, boolean header) {
// Java home dir is the one returned by "java.home" or parent dir of it in older JDKs.
File[] javaHomes = new File[2];
try {
javaHomes[0] = new File(System.getProperty("java.home")).getCanonicalFile();
javaHomes[1] = javaHomes[0].getParentFile().getCanonicalFile();
javaHomes[0] = Loader.getCanonicalFile(new File(System.getProperty("java.home")));
javaHomes[1] = Loader.getCanonicalFile(javaHomes[0].getParentFile());;
} catch (IOException | NullPointerException e) {
logger.warn("Could not include header files from java.home:" + e);
return;
Expand All @@ -170,7 +170,7 @@ void includeJavaPaths(ClassProperties properties, boolean header) {
}
for (File f : files) {
try {
f = f.getCanonicalFile();
f = Loader.getCanonicalFile(f);
} catch (IOException e) {
f = f.getAbsoluteFile();
}
Expand Down Expand Up @@ -228,8 +228,9 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
{
String p = properties.getProperty("platform.sysroot.prefix", "");
for (String s : properties.get("platform.sysroot")) {
if (new File(s).isDirectory()) {
s = new File(s).getCanonicalPath();
File file = new File(s);
if (file.isDirectory()) {
s = Loader.getCanonicalPath(file);
if (p.endsWith(" ")) {
command.add(p.trim()); command.add(s);
} else {
Expand All @@ -242,8 +243,9 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
{
String p = properties.getProperty("platform.toolchain.prefix", "");
for (String s : properties.get("platform.toolchain")) {
if (new File(s).isDirectory()) {
s = new File(s).getCanonicalPath();
File file = new File(s);
if (file.isDirectory()) {
s = Loader.getCanonicalPath(file);
if (p.endsWith(" ")) {
command.add(p.trim()); command.add(s);
} else {
Expand All @@ -256,8 +258,9 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
{
String p = properties.getProperty("platform.includepath.prefix", "");
for (String s : properties.get("platform.includepath")) {
if (new File(s).isDirectory()) {
s = new File(s).getCanonicalPath();
File file = new File(s);
if (file.isDirectory()) {
s = Loader.getCanonicalPath(file);
if (p.endsWith(" ")) {
command.add(p.trim()); command.add(s);
} else {
Expand All @@ -269,9 +272,9 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
for (File f : Loader.cacheResources(s)) {
if (f.isDirectory()) {
if (p.endsWith(" ")) {
command.add(p.trim()); command.add(f.getCanonicalPath());
command.add(p.trim()); command.add(Loader.getCanonicalPath(f));
} else {
command.add(p + f.getCanonicalPath());
command.add(p + Loader.getCanonicalPath(f));
}
}
}
Expand Down Expand Up @@ -319,8 +322,9 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
String p = properties.getProperty("platform.linkpath.prefix", "");
String p2 = properties.getProperty("platform.linkpath.prefix2");
for (String s : properties.get("platform.linkpath")) {
if (new File(s).isDirectory()) {
s = new File(s).getCanonicalPath();
File file = new File(s);
if (file.isDirectory()) {
s = Loader.getCanonicalPath(file);
if (p.endsWith(" ")) {
command.add(p.trim()); command.add(s);
} else {
Expand All @@ -339,15 +343,15 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
for (File f : Loader.cacheResources(s)) {
if (f.isDirectory()) {
if (p.endsWith(" ")) {
command.add(p.trim()); command.add(f.getCanonicalPath());
command.add(p.trim()); command.add(Loader.getCanonicalPath(f));
} else {
command.add(p + f.getCanonicalPath());
command.add(p + Loader.getCanonicalPath(f));
}
if (p2 != null) {
if (p2.endsWith(" ")) {
command.add(p2.trim()); command.add(f.getCanonicalPath());
command.add(p2.trim()); command.add(Loader.getCanonicalPath(f));
} else {
command.add(p2 + f.getCanonicalPath());
command.add(p2 + Loader.getCanonicalPath(f));
}
}
}
Expand Down Expand Up @@ -409,8 +413,9 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
{
String p = properties.getProperty("platform.frameworkpath.prefix", "");
for (String s : properties.get("platform.frameworkpath")) {
if (new File(s).isDirectory()) {
s = new File(s).getCanonicalPath();
File file = new File(s);
if (file.isDirectory()) {
s = Loader.getCanonicalPath(file);
if (p.endsWith(" ")) {
command.add(p.trim()); command.add(s);
} else {
Expand Down Expand Up @@ -468,7 +473,7 @@ int compile(String[] sourceFilenames, String outputFilename, ClassProperties pro
*/
File getOutputPath(Class[] classes, String[] sourcePrefixes) throws IOException {
cleanOutputDirectory();
File outputPath = outputDirectory != null ? outputDirectory.getCanonicalFile() : null;
File outputPath = outputDirectory != null ? Loader.getCanonicalFile(outputDirectory) : null;
ClassProperties p = Loader.loadProperties(classes, properties, true);
String platform = properties.getProperty("platform");
String extension = properties.getProperty("platform.extension");
Expand Down Expand Up @@ -498,7 +503,7 @@ File getOutputPath(Class[] classes, String[] sourcePrefixes) throws IOException
}
uri = new URI(packageURI);
boolean isFile = "file".equals(uri.getScheme());
File classPath = new File(classScanner.getClassLoader().getPaths()[0]).getCanonicalFile();
File classPath = Loader.getCanonicalFile(new File(classScanner.getClassLoader().getPaths()[0]));
// If our class is not a file, use first path of the user class loader as base for our output path
File packageDir = isFile ? new File(uri)
: new File(classPath, resourceName.substring(0, resourceName.lastIndexOf('/') + 1));
Expand Down Expand Up @@ -681,7 +686,7 @@ void createJar(File jarFile, String[] classPath, File ... files) throws IOExcept
// class to get the file as a resource from the ClassLoader.
String[] names = new String[classPath.length];
for (int i = 0; i < classPath.length; i++) {
String path = new File(classPath[i]).getCanonicalPath();
String path = Loader.getCanonicalPath(new File(classPath[i]));
if (name.startsWith(path)) {
names[i] = name.substring(path.length() + 1);
}
Expand Down Expand Up @@ -1022,7 +1027,7 @@ public File[] build() throws IOException, InterruptedException, ParserException
// Extract the required resources.
for (String s : resources.split(separator)) {
for (File f : Loader.cacheResources(s)) {
String path = f.getCanonicalPath();
String path = Loader.getCanonicalPath(f);
if (paths.length() > 0 && !paths.endsWith(File.pathSeparator)) {
paths += File.pathSeparator;
}
Expand All @@ -1032,7 +1037,7 @@ public File[] build() throws IOException, InterruptedException, ParserException
List<String> linkPaths = new ArrayList<String>();
for (String s2 : links.split(separator)) {
for (File f2 : Loader.cacheResources(s2)) {
String path2 = f2.getCanonicalPath();
String path2 = Loader.getCanonicalPath(f2);
if (path2.startsWith(path) && !path2.equals(path)) {
linkPaths.add(path2);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/bytedeco/javacpp/tools/CacheMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ String join(String separator, Iterable<String> strings) {
logger.info("Caching " + c);
File f = (File)cachePackage.invoke(c);
if (f != null) {
packages.add(f.getCanonicalPath());
packages.add(Loader.getCanonicalPath(f));
}
} catch (NoSuchMethodException e) {
// assume this class has no associated packages, skip
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/bytedeco/javacpp/tools/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -4106,7 +4106,7 @@ void parse(Context context,
}
if (file == null && includePath != null) {
for (String path : includePath) {
File f = new File(path, filename).getCanonicalFile();
File f = Loader.getCanonicalFile(new File(path, filename));
if (f.exists()) {
file = f;
break;
Expand Down Expand Up @@ -4269,7 +4269,7 @@ public File[] parse(File outputDirectory, String[] classPath, Class cls) throws
List<String> paths = allProperties.get("platform.includepath");
for (String s : allProperties.get("platform.includeresource")) {
for (File f : Loader.cacheResources(s)) {
paths.add(f.getCanonicalPath());
paths.add(Loader.getCanonicalPath(f));
}
}

Expand Down

0 comments on commit 28f89ad

Please sign in to comment.