Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 353aab1663d5f6e66396eaea0c4c42819ecf3696 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
/*******************************************************************************
 *  Copyright (c) 2008, 2017 EclipseSource and others.
 *
 *  This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License 2.0
 *  which accompanies this distribution, and is available at
 *  https://www.eclipse.org/legal/epl-2.0/
 *
 *  SPDX-License-Identifier: EPL-2.0
 * 
 *  Contributors:
 *      Eclipse Source - initial API and implementation
 *      IBM Corporation - ongoing development
 *******************************************************************************/
package org.eclipse.equinox.internal.p2.publisher.eclipse;

import java.io.*;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.p2.publisher.AbstractPublisherAction;
import org.eclipse.osgi.service.environment.Constants;
import org.eclipse.pde.internal.publishing.Activator;

public class ExecutablesDescriptor {

	private File location;
	private Set<File> files;
	private String executableName;
	private boolean temporary = false;
	private final String os;
	private File iniFile;

	public static File findExecutable(String os, File root, String baseName) {
		// TODO this may need to get more intelligent
		// if MacOS its going to be baseName.app/Contents/MacOS/baseName
		if (Constants.OS_MACOSX.equals(os)) {
			return new File(root, baseName + ".app/Contents/MacOS/" + baseName); //$NON-NLS-1$
		}
		// if it is not Mac and not Windows it must be a UNIX flavor
		if (!Constants.OS_WIN32.equals(os)) {
			return new File(root, baseName);
		}
		// otherwise we are left with windows
		return new File(root, baseName + ".exe"); //$NON-NLS-1$
	}

	/**
	 * Return the root directory of the executables folder for the given configSpec.  The folder
	 * is expected to be part of the standard Eclipse executables feature whose structure is 
	 * embedded here.
	 * @param executablesFeatureLocation the location of the executables feature
	 * @param configSpec the configuration to lookup
	 * @return the root location of the requested executables
	 */
	public static ExecutablesDescriptor createExecutablesFromFeature(File executablesFeatureLocation, String configSpec) {
		// TODO consider handling JAR'd features here...
		if (executablesFeatureLocation == null || !executablesFeatureLocation.exists())
			return null;
		String[] config = AbstractPublisherAction.parseConfigSpec(configSpec);
		File result = new File(executablesFeatureLocation, "bin/" + config[0] + "/" + config[1] + "/" + config[2]); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		if (!result.exists())
			return null;
		return new ExecutablesDescriptor(config[1], "launcher", result, new File[] {result}); //$NON-NLS-1$
	}

	/**
	 * Create an executable descriptor based on the given location, os and name.
	 * This method is typically used to identify the executable related files in existing
	 * unmanaged configurations.
	 * @param os
	 * @param location
	 * @param executable
	 * @return the created descriptor
	 */
	public static ExecutablesDescriptor createDescriptor(String os, String executable, File location) {
		if (Constants.OS_MACOSX.equals(os))
			return createMacDescriptor(os, executable, location);

		// if it is not Mac and not Windows it must be a UNIX flavor
		if (!Constants.OS_WIN32.equals(os))
			return createUnixDescriptor(os, executable, location);

		// Nothing else so it must be Windows
		return createWindowsDescriptor(os, executable, location);
	}

	private static ExecutablesDescriptor createWindowsDescriptor(String os, String executable, File location) {
		ExecutablesDescriptor result = new ExecutablesDescriptor(os, executable, location, null);
		File file = new File(location, executable + ".exe"); //$NON-NLS-1$
		if (file.isFile()) {
			result.addFile(file);
			result.iniFile = new File(location, executable + ".ini"); //$NON-NLS-1$
		}
		file = new File(location, "eclipsec.exe"); //$NON-NLS-1$
		if (file.isFile())
			result.addFile(file);
		return result;
	}

	private static ExecutablesDescriptor createUnixDescriptor(String os, String executable, File location) {
		ExecutablesDescriptor result = new ExecutablesDescriptor(os, executable, location, null);
		File[] files = location.listFiles();
		for (int i = 0; files != null && i < files.length; i++) {
			String extension = new Path(files[i].getName()).getFileExtension();
			if (files[i].isFile() && (extension == null || extension.equals("so"))) //$NON-NLS-1$
				result.addFile(files[i]);
		}
		result.iniFile = new File(location, executable + ".ini"); //$NON-NLS-1$
		return result;
	}

	private static ExecutablesDescriptor createMacDescriptor(String os, String executable, File location) {
		File files[] = location.listFiles((FilenameFilter) (dir, name) -> {
			int length = name.length();
			return length > 3 && name.substring(length - 4, length).equalsIgnoreCase(".app"); //$NON-NLS-1$
		});
		ExecutablesDescriptor result = new ExecutablesDescriptor(os, executable, location, files);
		result.iniFile = new File(location, executable + ".ini"); //$NON-NLS-1$
		return result;
	}

	public ExecutablesDescriptor(String os, String executable, File location, File[] files) {
		this.os = os;
		this.executableName = executable;
		this.location = location;
		if (files == null)
			this.files = new HashSet<>(11);
		else {
			this.files = new HashSet<>(files.length);
			for (File file : files) {
				addAllFiles(file);
			}
		}
	}

	public ExecutablesDescriptor(ExecutablesDescriptor descriptor) {
		this.os = descriptor.os;
		this.location = descriptor.location;
		this.executableName = descriptor.executableName;
		this.temporary = descriptor.temporary;
		this.files = new HashSet<>(descriptor.files);
	}

	public void addAllFiles(File file) {
		if (file.isFile())
			files.add(relativize(file));
		else {
			File absolute = file.isAbsolute() ? file : new File(location, file.getPath());
			File[] list = absolute.listFiles();
			for (File list1 : list) {
				addAllFiles(list1);
			}
		}
	}

	public void addFile(File file) {
		files.add(relativize(file));
	}

	// do a simple relativization by removing all the bits before the location
	private File relativize(File file) {
		if (!file.isAbsolute())
			return file;
		String path = file.getPath();
		if (!path.startsWith(location.getPath()))
			throw new IllegalArgumentException(file.toString() + " must be related to " + location); //$NON-NLS-1$
		path = path.substring(location.getPath().length());
		// trim off any separator.  This accomodates people who set the location with a trailing /
		if (path.startsWith("/") || path.startsWith("\\")) //$NON-NLS-1$//$NON-NLS-2$
			path = path.substring(1);
		return new File(path);
	}

	public void removeFile(File file) {
		files.remove(relativize(file));
	}

	public void replace(File oldFile, File newFile) {
		removeFile(oldFile);
		addFile(newFile);
	}

	public File[] getFiles() {
		File[] result = files.toArray(new File[files.size()]);
		for (int i = 0; i < result.length; i++)
			result[i] = new File(location, result[i].getPath());
		return result;
	}

	public String getExecutableName() {
		return executableName;
	}

	public File getExecutable() {
		return findExecutable(os, location, executableName);
	}

	public File getIniLocation() {
		return iniFile;
	}

	public File getLocation() {
		return location;
	}

	public void setLocation(File value) {
		location = value;
	}

	public boolean isTemporary() {
		return temporary;
	}

	public void setExecutableName(String value, boolean updateFiles) {
		if (updateFiles)
			updateExecutableName(value);
		executableName = value;
	}

	public void makeTemporaryCopy() {
		if (isTemporary())
			return;
		File tempFile = null;
		try {
			tempFile = File.createTempFile("p2.brandingIron", ""); //$NON-NLS-1$ //$NON-NLS-2$
			tempFile.delete();
			for (File file : files)
				FileUtils.copy(location, tempFile, file, true);
		} catch (IOException e) {
			LogHelper.log(new Status(IStatus.ERROR, Activator.ID, "Error publishing artifacts", e)); //$NON-NLS-1$
		}
		location = tempFile;
		temporary = true;
	}

	/**
	 * If the executable represented by this descriptor has been branded then a mess
	 * of files have been renamed.  Here scan the descriptor's file list and update the names
	 * taking into account the different layout on different OSes.
	 * @param newName the new name of the executable.
	 */
	private void updateExecutableName(String newName) {
		if (newName.equalsIgnoreCase(executableName))
			return;
		String targetIni = executableName + ".ini"; //$NON-NLS-1$
		String targetExecutable = executableName;
		String executableExtension = Constants.OS_WIN32.equals(os) ? ".exe" : ""; //$NON-NLS-1$ //$NON-NLS-2$
		targetExecutable = executableName + executableExtension;
		Set<File> filesCopy = new HashSet<>(files);
		for (File file : filesCopy) {
			String base = file.getParent();

			// use String concatenation here because new File("", "foo") is absolute on at least windows...
			base = base == null ? "" : base + "/"; //$NON-NLS-1$ //$NON-NLS-2$
			if (Constants.OS_MACOSX.equals(os) && base.startsWith(executableName + ".app")) //$NON-NLS-1$
				base = newName + ".app" + base.substring(executableName.length() + 4); //$NON-NLS-1$
			if (file.getName().equalsIgnoreCase(targetExecutable))
				replace(file, new File(base + newName + executableExtension));
			else if (file.getName().equalsIgnoreCase(targetIni))
				replace(file, new File(base + newName + ".ini")); //$NON-NLS-1$
			else if (Constants.OS_MACOSX.equals(os))
				replace(file, new File(base + file.getName()));
		}
	}
}

Back to the top