2
0

Refactor Native to try and load other architectures.

This commit is contained in:
Maarten Billemont 2018-08-24 16:33:14 -04:00
parent d5551c8c8c
commit 3cbb063926

View File

@ -18,14 +18,14 @@
package com.lyndir.masterpassword.impl; package com.lyndir.masterpassword.impl;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.*;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import java.io.*; import java.io.*;
import java.util.Locale; import java.util.*;
import java.util.function.Predicate;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -43,71 +43,161 @@ public final class Native {
private static final String NATIVES_PATH = "lib"; private static final String NATIVES_PATH = "lib";
@SuppressWarnings({ "HardcodedFileSeparator", "LoadLibraryWithNonConstantString" }) @SuppressWarnings({ "HardcodedFileSeparator", "LoadLibraryWithNonConstantString" })
public static void load(final Class<?> context, final String name) { public static boolean load(final Class<?> context, final String name) {
// Try to load the library using the native system. // Try to load the library using the native system.
try { try {
System.loadLibrary( name ); System.loadLibrary( name );
return; return true;
} }
catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) { catch (@SuppressWarnings("ErrorNotRethrown") final UnsatisfiedLinkError ignored) {
} }
// Try to find and open a stream to the packaged library resource. // Try to find and open a stream to the packaged library resource.
try { String library = System.mapLibraryName( name );
String library = System.mapLibraryName( name ); int libraryDot = library.lastIndexOf( EXTENSION_SEPARATOR );
int libraryDot = library.lastIndexOf( EXTENSION_SEPARATOR ); String libraryName = (libraryDot > 0)? library.substring( 0, libraryDot ): library;
String libraryName = (libraryDot > 0)? library.substring( 0, libraryDot ): library; String libraryExtension = (libraryDot > 0)? library.substring( libraryDot ): ".lib";
String libraryExtension = (libraryDot > 0)? library.substring( libraryDot ): ".lib";
String libraryResource = getLibraryResource( library );
InputStream libraryStream = context.getResourceAsStream( libraryResource );
if (libraryStream == null)
throw new IllegalStateException(
"Library: " + name + " (" + libraryResource + "), not found in class loader for: " + context );
// Write the library resource to a temporary file. @Nullable
File libraryFile = File.createTempFile( libraryName, libraryExtension ); File libraryFile = null;
FileOutputStream libraryFileStream = new FileOutputStream( libraryFile ); Set<String> libraryResources = getLibraryResources( library );
for (final String libraryResource : libraryResources) {
try { try {
libraryFile.deleteOnExit(); InputStream libraryStream = context.getResourceAsStream( libraryResource );
ByteStreams.copy( libraryStream, libraryFileStream ); if (libraryStream == null) {
} logger.dbg( "No resource for library: %s", libraryResource );
finally { continue;
libraryFileStream.close(); }
libraryStream.close();
}
// Load the library from the temporary file. // Write the library resource to a temporary file.
System.load( libraryFile.getAbsolutePath() ); libraryFile = File.createTempFile( libraryName, libraryExtension );
} FileOutputStream libraryFileStream = new FileOutputStream( libraryFile );
catch (final IOException e) { try {
throw new IllegalStateException( "Couldn't extract library: " + name, e ); libraryFile.deleteOnExit();
ByteStreams.copy( libraryStream, libraryFileStream );
}
finally {
libraryFileStream.close();
libraryStream.close();
}
// Load the library from the temporary file.
System.load( libraryFile.getAbsolutePath() );
return true;
}
catch (@SuppressWarnings("ErrorNotRethrown") final IOException | UnsatisfiedLinkError e) {
logger.dbg( e, "Couldn't load library: %s", libraryResource );
if (libraryFile != null)
if (libraryFile.exists() && !libraryFile.delete())
logger.wrn( "Couldn't clean up library file: %s", libraryFile );
libraryFile = null;
}
} }
return false;
} }
@Nonnull @Nonnull
private static String getLibraryResource(final String library) { private static Set<String> getLibraryResources(final String library) {
String system = ifNotNullElse( System.getProperty( "os.name" ), "linux" ).toLowerCase( Locale.ROOT );
String architecture = ifNotNullElse( System.getProperty( "os.arch" ), "x86" ).toLowerCase( Locale.ROOT );
// Standardize system naming in accordance with masterpassword-core. // Standardize system naming in accordance with masterpassword-core.
if (system.contains( "windows" )) Sys system = Sys.findCurrent();
system = "windows";
else if (system.contains( "mac os x" ) || system.contains( "darwin" ) || system.contains( "osx" ))
system = "macos";
else
system = "linux";
// Standardize architecture naming in accordance with masterpassword-core. // Standardize architecture naming in accordance with masterpassword-core.
if (ImmutableList.of( "arm", "arm-v7", "armv7", "arm32" ).contains( architecture )) Collection<Arch> architectures = new LinkedHashSet<>();
architecture = "arm"; architectures.add( Arch.findCurrent() );
else if (architecture.startsWith( "arm" )) architectures.addAll( Arrays.asList( Arch.values() ) );
architecture = "arm64";
else if (ImmutableList.of( "x86_64", "amd64", "x64", "x86-64" ).contains( architecture ))
architecture = "x86_64";
else
architecture = "x86";
return Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, architecture, library ); ImmutableSet.Builder<String> resources = ImmutableSet.builder();
for (final Arch arch : architectures)
resources.add( Joiner.on( RESOURCE_SEPARATOR ).join( "", NATIVES_PATH, system, arch, library ) );
return resources.build();
}
private enum Sys implements Predicate<String> {
windows {
@Override
public boolean test(final String system) {
return system.contains( "windows" );
}
},
macos {
@Override
public boolean test(final String system) {
return system.contains( "mac os x" ) || system.contains( "darwin" ) || system.contains( "osx" );
}
},
linux {
@Override
public boolean test(final String system) {
return system.contains( "linux" );
}
};
@Nonnull
public static Sys findCurrent() {
return find( System.getProperty( "os.name" ) );
}
@Nonnull
public static Sys find(@Nullable String name) {
if (name != null) {
name = name.toLowerCase( Locale.ROOT );
for (final Sys sys : values())
if (sys.test( name ))
return sys;
}
return linux;
}
}
private enum Arch implements Predicate<String> {
arm {
@Override
public boolean test(final String architecture) {
return ImmutableList.of( "arm", "arm-v7", "armv7", "arm32" ).contains( architecture );
}
},
arm64 {
@Override
public boolean test(final String architecture) {
return architecture.startsWith( "arm" ) && !arm.test( architecture );
}
},
x86_64 {
@Override
public boolean test(final String architecture) {
return ImmutableList.of( "x86_64", "amd64", "x64", "x86-64" ).contains( architecture );
}
},
x86 {
@Override
public boolean test(final String architecture) {
return ImmutableList.of( "x86", "i386", "i686" ).contains( architecture );
}
};
@Nonnull
public static Arch findCurrent() {
return find( System.getProperty( "os.arch" ) );
}
@Nonnull
public static Arch find(@Nullable String name) {
if (name != null) {
name = name.toLowerCase( Locale.ROOT );
for (final Arch arch : values())
if (arch.test( name ))
return arch;
}
return x86;
}
} }
} }