]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/internal/Library.java
eab183c8924af8035613ada9992cbdc4e107559c
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / internal / Library.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.internal;
15
16 import java.io.*;
17 import java.lang.reflect.*;
18 import java.net.*;
19 import java.nio.file.*;
20 import java.util.function.*;
21 import java.util.jar.*;
22
23 public class Library {
24
25         /* SWT Version - Mmmm (M=major, mmm=minor) */
26
27         /**
28          * SWT Major version number (must be >= 0)
29          */
30         static int MAJOR_VERSION = 4;
31
32         /**
33          * SWT Minor version number (must be in the range 0..999)
34          */
35         static int MINOR_VERSION = 930;
36
37         /**
38          * SWT revision number (must be >= 0)
39          */
40         static int REVISION = 7;
41
42         /**
43          * The JAVA and SWT versions
44          */
45         public static final int JAVA_VERSION, SWT_VERSION;
46         public static final String USER_HOME;
47
48         static final String SEPARATOR;
49         static final String DELIMITER;
50
51         static final String JAVA_LIB_PATH = "java.library.path";
52         static final String SWT_LIB_PATH = "swt.library.path";
53
54
55         /* 64-bit support */
56         static final boolean IS_64 = longConst() == (long /*int*/)longConst();
57         static final String SUFFIX_64 = "-64";  //$NON-NLS-1$
58         static final String SWT_LIB_DIR;
59
60 static {
61         DELIMITER = System.getProperty("line.separator"); //$NON-NLS-1$
62         SEPARATOR = File.separator;
63         USER_HOME = System.getProperty ("user.home");
64         SWT_LIB_DIR = ".swt" + SEPARATOR + "lib" + SEPARATOR + os() + SEPARATOR + arch(); //$NON-NLS-1$ $NON-NLS-2$
65         JAVA_VERSION = parseVersion(System.getProperty("java.version")); //$NON-NLS-1$
66         SWT_VERSION = SWT_VERSION(MAJOR_VERSION, MINOR_VERSION);
67 }
68
69 static String arch() {
70         String osArch = System.getProperty("os.arch"); //$NON-NLS-1$
71         if (osArch.equals ("i386") || osArch.equals ("i686")) return "x86"; //$NON-NLS-1$ $NON-NLS-2$ $NON-NLS-3$
72         if (osArch.equals ("amd64")) return "x86_64"; //$NON-NLS-1$ $NON-NLS-2$
73         return osArch;
74 }
75
76 static String os() {
77         String osName = System.getProperty("os.name"); //$NON-NLS-1$
78         if (osName.equals ("Linux")) return "linux"; //$NON-NLS-1$ $NON-NLS-2$
79         if (osName.equals ("Mac OS X")) return "macosx"; //$NON-NLS-1$ $NON-NLS-2$
80         if (osName.startsWith ("Win")) return "win32"; //$NON-NLS-1$ $NON-NLS-2$
81         return osName;
82 }
83
84 static void chmod(String permision, String path) {
85         if (os().equals ("win32")) return; //$NON-NLS-1$
86         try {
87                 Runtime.getRuntime ().exec (new String []{"chmod", permision, path}).waitFor(); //$NON-NLS-1$
88         } catch (Throwable e) {}
89 }
90
91 /* Use method instead of in-lined constants to avoid compiler warnings */
92 static long longConst() {
93         return 0x1FFFFFFFFL;
94 }
95
96 static int parseVersion(String version) {
97         if (version == null) return 0;
98         int major = 0, minor = 0, micro = 0;
99         int length = version.length(), index = 0, start = 0;
100         while (index < length && Character.isDigit(version.charAt(index))) index++;
101         try {
102                 if (start < length) major = Integer.parseInt(version.substring(start, index));
103         } catch (NumberFormatException e) {}
104         start = ++index;
105         while (index < length && Character.isDigit(version.charAt(index))) index++;
106         try {
107                 if (start < length) minor = Integer.parseInt(version.substring(start, index));
108         } catch (NumberFormatException e) {}
109         start = ++index;
110         while (index < length && Character.isDigit(version.charAt(index))) index++;
111         try {
112                 if (start < length) micro = Integer.parseInt(version.substring(start, index));
113         } catch (NumberFormatException e) {}
114         return JAVA_VERSION(major, minor, micro);
115 }
116
117 /**
118  * Returns the Java version number as an integer.
119  *
120  * @param major
121  * @param minor
122  * @param micro
123  * @return the version
124  */
125 public static int JAVA_VERSION (int major, int minor, int micro) {
126         return (major << 16) + (minor << 8) + micro;
127 }
128
129 /**
130  * Returns the SWT version number as an integer.
131  *
132  * @param major
133  * @param minor
134  * @return the version
135  */
136 public static int SWT_VERSION (int major, int minor) {
137         return major * 1000 + minor;
138 }
139
140 private static boolean extractResource(String resourceName, File outFile) {
141         try (InputStream inputStream = Library.class.getResourceAsStream (resourceName)) {
142                 if (inputStream == null) return false;
143                 Files.copy(inputStream, outFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
144         } catch (Throwable e) {
145                 return false;
146         }
147
148         return true;
149 }
150
151 /**
152  * Extract file with 'mappedName' into path 'extractToFilePath'.
153  * Does not overwrite existing file.
154  * Does not leave trash on error.
155  * @param extractToFilePath full path of where the file is to be extacted to, inc name of file,
156  *                          e.g /home/USER/.swt/lib/linux/x86_64/libswt-MYLIB-gtk-4826.so
157  * @param mappedName file to be searched in jar.
158  * @return      true upon success, failure if something went wrong.
159  */
160 static boolean extract (String extractToFilePath, String mappedName) {
161         File file = new File(extractToFilePath);
162         if (file.exists ()) return true;
163
164         // Write to temp file first, so that other processes don't see
165         // partially written library on disk
166         File tempFile;
167         try {
168                 tempFile = File.createTempFile (file.getName(), ".tmp", file.getParentFile()); //$NON-NLS-1$
169         } catch (Throwable e) {
170                 return false;
171                                 }
172
173         // Extract resource
174         String resourceName = "/" + mappedName; //$NON-NLS-1$
175         if (!extractResource (resourceName, tempFile)) {
176                 tempFile.delete();
177                 return false;
178                         }
179
180         // Make it executable
181         chmod ("755", tempFile.getPath()); //$NON-NLS-1$
182
183         // "Publish" file now that it's ready to use.
184         // If there is a file already, then someone published while we were
185         // extracting, just delete our file and consider it a success.
186         try {
187                 Files.move (tempFile.toPath(), file.toPath());
188         } catch (Throwable e) {
189                 tempFile.delete();
190         }
191
192         return true;
193 }
194
195 static boolean isLoadable () {
196         URL url = Platform.class.getClassLoader ().getResource ("org/eclipse/swt/internal/Library.class"); //$NON-NLS-1$
197         if (!url.getProtocol ().equals ("jar")) { //$NON-NLS-1$
198                 /* SWT is presumably running in a development environment */
199                 return true;
200         }
201
202         Attributes attributes = null;
203         try {
204                 URLConnection connection = url.openConnection();
205                 if (!(connection instanceof JarURLConnection)) {
206                         /* should never happen for a "jar:" url */
207                         return false;
208                 }
209                 JarURLConnection jc = (JarURLConnection) connection;
210                 attributes = jc.getMainAttributes();
211         } catch (IOException e) {
212                 /* should never happen for a valid SWT jar with the expected manifest values */
213                 return false;
214         }
215
216         String os = os ();
217         String arch = arch ();
218         String manifestOS = attributes.getValue ("SWT-OS"); //$NON-NLS-1$
219         String manifestArch = attributes.getValue ("SWT-Arch"); //$NON-NLS-1$
220         if (arch.equals (manifestArch) && os.equals (manifestOS)) {
221                 return true;
222         }
223
224         return false;
225 }
226
227 static boolean load (String libName, StringBuilder message) {
228         try {
229                 if (libName.contains (SEPARATOR)) {
230                         System.load (libName);
231                 } else {
232                         System.loadLibrary (libName);
233                 }
234                 return true;
235         } catch (UnsatisfiedLinkError e) {
236                 if (message.length() == 0) message.append(DELIMITER);
237                 message.append('\t');
238                 message.append(e.getMessage());
239                 message.append(DELIMITER);
240         }
241         return false;
242 }
243
244 /**
245  * Loads the shared library that matches the version of the
246  * Java code which is currently running.  SWT shared libraries
247  * follow an encoding scheme where the major, minor and revision
248  * numbers are embedded in the library name and this along with
249  * <code>name</code> is used to load the library.  If this fails,
250  * <code>name</code> is used in another attempt to load the library,
251  * this time ignoring the SWT version encoding scheme.
252  *
253  * @param name the name of the library to load
254  */
255 public static void loadLibrary (String name) {
256         loadLibrary (name, true);
257 }
258
259 /**
260  * Loads the shared library that matches the version of the
261  * Java code which is currently running.  SWT shared libraries
262  * follow an encoding scheme where the major, minor and revision
263  * numbers are embedded in the library name and this along with
264  * <code>name</code> is used to load the library.  If this fails,
265  * <code>name</code> is used in another attempt to load the library,
266  * this time ignoring the SWT version encoding scheme.
267  *
268  * @param name the name of the library to load
269  * @param mapName true if the name should be mapped, false otherwise
270  */
271 public static void loadLibrary (String name, boolean mapName) {
272         String prop = System.getProperty ("sun.arch.data.model"); //$NON-NLS-1$
273         if (prop == null) prop = System.getProperty ("com.ibm.vm.bitmode"); //$NON-NLS-1$
274         if (prop != null) {
275                 if ("32".equals (prop) && IS_64) { //$NON-NLS-1$
276                         throw new UnsatisfiedLinkError ("Cannot load 64-bit SWT libraries on 32-bit JVM"); //$NON-NLS-1$
277                 }
278                 if ("64".equals (prop) && !IS_64) { //$NON-NLS-1$
279                         throw new UnsatisfiedLinkError ("Cannot load 32-bit SWT libraries on 64-bit JVM"); //$NON-NLS-1$
280                 }
281         }
282
283         /* Compute the library name and mapped name */
284         String libName1, libName2, mappedName1, mappedName2;
285         if (mapName) {
286                 String version = getVersionString ();
287                 libName1 = name + "-" + Platform.PLATFORM + "-" + version;  //$NON-NLS-1$ //$NON-NLS-2$
288                 libName2 = name + "-" + Platform.PLATFORM;  //$NON-NLS-1$
289                 mappedName1 = mapLibraryName (libName1);
290                 mappedName2 = mapLibraryName (libName2);
291         } else {
292                 libName1 = libName2 = mappedName1 = mappedName2 = name;
293         }
294
295         StringBuilder message = new StringBuilder();
296
297         /* Try loading library from swt library path */
298         String path = System.getProperty (SWT_LIB_PATH); //$NON-NLS-1$
299         if (path != null) {
300                 path = new File (path).getAbsolutePath ();
301                 if (load (path + SEPARATOR + mappedName1, message)) return;
302                 if (mapName && load (path + SEPARATOR + mappedName2, message)) return;
303         }
304
305         /* Try loading library from java library path */
306         if (load (libName1, message)) return;
307         if (mapName && load (libName2, message)) return;
308
309         /* Try loading library from the tmp directory if swt library path is not specified.
310          * Create the tmp folder if it doesn't exist. Tmp folder looks like this:
311          * ~/.swt/lib/<platform>/<arch>/
312          */
313         String fileName1 = mappedName1;
314         String fileName2 = mappedName2;
315         if (path == null) {
316                 path = USER_HOME;
317                 File dir = new File (path, SWT_LIB_DIR);
318                 if ((dir.exists () && dir.isDirectory ()) || dir.mkdirs ()) { // Create if not exist.
319                         path = dir.getAbsolutePath ();
320                 } else {
321                         /* fall back to using the home dir directory */
322                         if (IS_64) {
323                                 fileName1 = mapLibraryName (libName1 + SUFFIX_64);
324                                 fileName2 = mapLibraryName (libName2 + SUFFIX_64);
325                         }
326                 }
327                 if (load (path + SEPARATOR + fileName1, message)) return;
328                 if (mapName && load (path + SEPARATOR + fileName2, message)) return;
329         }
330
331         /* Try extracting and loading library from jar. */
332         if (path != null) {
333                 if (extract (path + SEPARATOR + fileName1, mappedName1)) {
334                         load(path + SEPARATOR + fileName1, message);
335                         return;
336                 }
337                 if (mapName && extract (path + SEPARATOR + fileName2, mappedName2)) {
338                         load(path + SEPARATOR + fileName2, message);
339                         return;
340                 }
341         }
342
343         /* Failed to find the library */
344         throw new UnsatisfiedLinkError ("Could not load SWT library. Reasons: " + message.toString()); //$NON-NLS-1$
345 }
346
347 static String mapLibraryName (String libName) {
348         /* SWT libraries in the Macintosh use the extension .jnilib but the some VMs map to .dylib. */
349         libName = System.mapLibraryName (libName);
350         String ext = ".dylib"; //$NON-NLS-1$
351         if (libName.endsWith(ext)) {
352                 libName = libName.substring(0, libName.length() - ext.length()) + ".jnilib"; //$NON-NLS-1$
353         }
354         return libName;
355 }
356
357 /**
358  * @return String Combined SWT version like 4826
359  */
360 public static String getVersionString () {
361         String version = System.getProperty ("swt.version"); //$NON-NLS-1$
362         if (version == null) {
363                 version = "" + MAJOR_VERSION; //$NON-NLS-1$
364                 /* Force 3 digits in minor version number */
365                 if (MINOR_VERSION < 10) {
366                         version += "00"; //$NON-NLS-1$
367                 } else {
368                         if (MINOR_VERSION < 100) version += "0"; //$NON-NLS-1$
369                 }
370                 version += MINOR_VERSION;
371                 /* No "r" until first revision */
372                 if (REVISION > 0) version += "r" + REVISION; //$NON-NLS-1$
373         }
374         return version;
375 }
376
377
378 /**
379  * Locates a resource located either in java library path, swt library path, or attempts to extract it from inside swt.jar file.
380  * This function supports a single level subfolder, e.g SubFolder/resource.
381  *
382  * Dev note: (17/12/07) This has been developed and throughly tested on GTK. Designed to work on Cocoa/Win as well, but not tested.
383  *
384  * @param subDir  'null' or a folder name without slashes. E.g Correct: 'mysubdir',  incorrect: '/subdir/'.
385  *                Platform specific Slashes will be added automatically.
386  * @param resourceName e.g swt-webkitgtk
387  * @param mapResourceName  true if you like platform specific mapping applied to resource name. e.g  MyLib -> libMyLib-gtk-4826.so
388  */
389 public static File findResource(String subDir, String resourceName, boolean mapResourceName){
390
391         //We construct a 'maybe' subdirectory path. 'Maybe' because if no subDir given, then it's an empty string "".
392                                                                                  //       subdir  e.g:  subdir
393         String maybeSubDirPath = subDir != null ? subDir + SEPARATOR : "";       //               e.g:  subdir/  or ""
394         String maybeSubDirPathWithPrefix = subDir != null ? SEPARATOR + maybeSubDirPath : ""; //  e.g: /subdir/  or ""
395         final String finalResourceName = mapResourceName ?
396                         mapLibraryName(resourceName + "-" + Platform.PLATFORM + "-" + getVersionString ()) // e.g libMyLib-gtk-3826.so
397                         : resourceName;
398
399         // 1) Look for the resource in the java/swt library path(s)
400         // This code commonly finds the resource if the swt project is a required project and the swt binary (for your platform)
401         // project is open in your workplace  (found in the JAVA_LIBRARY_PATH) or if you're explicitly specified SWT_LIBRARY_PATH.
402         {
403                 Function<String, File> lookForFileInPath = searchPath -> {
404                         String classpath = System.getProperty(searchPath);
405                         if (classpath != null){
406                                 String[] paths = classpath.split(":");
407                                 for (String path : paths) {
408                                 File file = new File(path + SEPARATOR + maybeSubDirPath + finalResourceName);
409                                         if (file.exists()){
410                                                 return file;
411                                         }
412                                 }
413                         }
414                         return null;
415                 };
416                 File result = null;
417                 for (String path : new String[] {JAVA_LIB_PATH,SWT_LIB_PATH}) {
418                         result = lookForFileInPath.apply(path);
419                         if (result != null)
420                                 return result;
421                 }
422         }
423
424         // 2) If SWT is ran as OSGI bundle (e.g inside Eclipse), then local resources are extracted to
425         // eclipse/configuration/org.eclipse.osgi/NN/N/.cp/<resource> and we're given a pointer to the file.
426         {
427                 // If this is an OSGI bundle look for the resource using getResource
428                 URL url = Library.class.getClassLoader().getResource(maybeSubDirPathWithPrefix + finalResourceName);
429                 URLConnection connection;
430                 try {
431                         connection = url.openConnection();
432                         Method getFileURLMethod = connection.getClass().getMethod("getFileURL");
433                         if (getFileURLMethod != null){
434                                 // This method does the actual extraction of file to: ../eclipse/configuration/org.eclipse.osgi/NN/N/.cp/<SubDir>/resource.ext
435                                 URL result = (URL) getFileURLMethod.invoke(connection);
436                                 return new File(result.toURI());
437                         }
438                 } catch (Exception e) {
439                         // If any exceptions are thrown the resource cannot be located this way.
440                 }
441         }
442
443         // 3) Need to try to pull the resource out of the swt.jar.
444         // Look for the resource in the user's home directory, (if already extracted in the temp swt folder. (~/.swt/lib...)
445         // Extract from the swt.jar if not there already.
446         {
447                 // Developer note:
448                 // To test this piece of code, you need to compile SWT into a jar and use it in a test project. E.g
449                 //   cd ~/git/eclipse.platform.swt.binaries/bundles/org.eclipse.swt.gtk.linux.x86_64/
450                 //   mvn clean verify -Pbuild-individual-bundles -Dnative=gtk.linux.x86_64
451                 // then ./target/ will contain org.eclipse.swt.gtk.linux.x86_64-3.106.100-SNAPSHOT.jar (and it's source),
452                 //  you can copy those into your test swt project and test that your resource is extracted into something like ~/.swt/...
453                 // Lastly, if using subDir, you need to edit the build.properties and specify the folder you wish to have included in your jar in the includes.
454                 File file = new File (USER_HOME + SEPARATOR +  SWT_LIB_DIR + maybeSubDirPathWithPrefix, finalResourceName);
455                 if (file.exists()){
456                         return file;
457                 } else { // Try to extract file from jar if not found.
458
459                         // Create temp directory if it doesn't exist
460                         File tempDir = new File (USER_HOME, SWT_LIB_DIR + maybeSubDirPathWithPrefix);
461                         if ((!tempDir.exists () || tempDir.isDirectory ())) {
462                                 tempDir.mkdirs ();
463                         }
464
465                         if (extract(file.getPath(), maybeSubDirPath + finalResourceName)) {
466                                 if (file.exists()) {
467                                         return file;
468                                 }
469                         }
470                 }
471         }
472         throw new UnsatisfiedLinkError("Could not find resource" + resourceName +  (subDir != null ? " (in subdirectory: " + subDir + " )" : ""));
473 }
474
475
476 }