/*
* Copyright 2012 Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.core.model;
import com.google.dart.engine.internal.sdk.LibraryMap;
import com.google.dart.engine.sdk.DirectoryBasedDartSdk;
import com.google.dart.engine.utilities.io.FileUtilities;
import com.google.dart.engine.utilities.os.OSUtilities;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.core.DartCoreDebug;
import com.google.dart.tools.core.utilities.download.DownloadUtilities;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
// http://commondatastorage.googleapis.com/dart-editor-archive-integration/latest/dartsdk-macos-32.zip
// TOOD(devoncarew): refactor the download/unzip/copy code into utility methods (to be
// shared with the update code).
/**
* The clearing house for getting the current SDK and listening for SDK changes.
*
* @coverage dart.tools.core.model
*/
public class DartSdkManager {
/**
* Environment variable key for user-specified update URLs.
*/
public static final String UPDATE_URL_ENV_VAR = "com.dart.tools.update.core.url";
private static final String DEFAULT_UPDATE_URL = "http://dartlang.org/editor/update/channels/dev/";
private static final String USER_DEFINED_SDK_KEY = "dart.sdk";
private static final String USER_DEFINED_DARTIUM_KEY = "dart.chromium";
private static final String SDK_ZIP = "latest/sdk/dartsdk-{0}-{1}-release.zip";
/**
* The name of the directory on non-Mac that contains dartium.
*/
private static final String DARTIUM_DIRECTORY_NAME = "chromium"; //$NON-NLS-1$
/**
* The name of the file containing the Dartium executable on Linux.
*/
private static final String DARTIUM_EXECUTABLE_NAME_LINUX = "chrome"; //$NON-NLS-1$
/**
* The name of the file containing the Dartium executable on Macintosh.
*/
private static final String DARTIUM_EXECUTABLE_NAME_MAC = "Chromium.app/Contents/MacOS/Chromium"; //$NON-NLS-1$
/**
* The name of the file containing the Dartium executable on Windows.
*/
private static final String DARTIUM_EXECUTABLE_NAME_WIN = "Chrome.exe"; //$NON-NLS-1$
/**
* A special instance of {@link com.google.dart.engine.sdk.DartSdk} representing missing SDK.
*/
public static final DirectoryBasedDartSdk NONE = new DirectoryBasedDartSdk(new File(
getEclipseInstallationDirectory(),
"no-dart-sdk")) {
@Override
protected LibraryMap initialLibraryMap(boolean useDart2jsPaths) {
return new LibraryMap();
}
};
private static final String SDK_DIR_NAME = "dart-sdk";
private static DartSdkManager manager = new DartSdkManager();
public static File getEclipseInstallationDirectory() {
return new File(Platform.getInstallLocation().getURL().getFile());
}
public static DartSdkManager getManager() {
return manager;
}
/**
* The Editor looks for "dart-sdk" as a sibling to the installation directory.
*
* @return
*/
private static File getDefaultEditorSdkDirectory() {
File parent = getEclipseInstallationDirectory().getParentFile();
return new File(parent, SDK_DIR_NAME);
}
/**
* The plugins build looks in the installation directory for "dart-sdk".
*
* @return
*/
private static File getDefaultPluginsSdkDirectory() {
return new File(getEclipseInstallationDirectory(), SDK_DIR_NAME);
}
/**
* @return one of ia32 or x64
*/
private static String getPlatformBititude() {
if (DartCore.is32Bit()) {
return "ia32";
} else {
return "x64";
}
}
/**
* @return one of macos, windows, or linux
*/
private static String getPlatformCode() {
if (DartCore.isWindows()) {
return "windows";
} else if (DartCore.isMac()) {
return "macos";
} else if (DartCore.isLinux()) {
return "linux";
}
return null;
}
/**
* The file containing the Dartium executable.
*/
private File dartiumExecutable;
/**
* The directory containing the Dartium executable.
*/
private File dartiumDirectory;
private DirectoryBasedDartSdk sdk;
private String sdkContextId;
private List<DartSdkListener> listeners = new ArrayList<DartSdkListener>();
private DartSdkManager() {
initSdk();
}
public void addSdkListener(DartSdkListener lisener) {
listeners.add(lisener);
}
/**
* Return the file containing the Dartium executable, or {@code null} if it does not exist.
*
* @return the file containing the Dartium executable
*/
public File getDartiumExecutable() {
synchronized (this) {
if (dartiumExecutable == null) {
dartiumExecutable = FileUtilities.verifyExecutable(new File(
getDartiumWorkingDirectory(),
getDartiumBinaryName()));
}
}
return dartiumExecutable;
}
/**
* Return the directory where dartium can be found (the directory that will be the working
* directory is Dartium is invoked without changing the default).
*
* @return the directory where dartium can be found
*/
public File getDartiumWorkingDirectory() {
if (dartiumDirectory == null) {
File dartiumDir = getUserDefinedDirectory(USER_DEFINED_DARTIUM_KEY);
if (dartiumDir != null && !dartiumDir.exists()) {
DartCore.logError(USER_DEFINED_SDK_KEY + " defined in " + DartCore.EDITOR_PROPERTIES
+ " but does not exist: " + dartiumDir);
dartiumDir = null;
}
if (dartiumDir == null) {
dartiumDir = getDirectoryFromPath(DartCore.getPlugin().getDartiumLocationPref());
if (dartiumDir != null && !dartiumDir.exists()) {
dartiumDir = null;
}
}
if (dartiumDir == null) {
dartiumDir = new File(getEclipseInstallationDirectory(), DARTIUM_DIRECTORY_NAME);
}
if (dartiumDir.exists()) {
dartiumDirectory = dartiumDir;
}
}
return dartiumDirectory;
}
/**
* Return the directory where dartium can be found (the directory that will be the working
* directory is Dartium is invoked without changing the default).
*
* @param installDir the installation directory
* @return the directory where dartium can be found
*/
public File getDartiumWorkingDirectory(File installDir) {
return new File(installDir, DARTIUM_DIRECTORY_NAME);
}
public DirectoryBasedDartSdk getSdk() {
return sdk;
}
public String getSdkContextId() {
return sdkContextId;
}
public String getUpdateChannelUrl() {
try {
File file = getUpdatePropertiesFile();
if (file.exists()) {
Properties properties = new Properties();
properties.load(new FileReader(file));
return properties.getProperty(UPDATE_URL_ENV_VAR);
}
} catch (FileNotFoundException e) {
DartCore.logError(e);
} catch (IOException e) {
DartCore.logError(e);
} catch (URISyntaxException e) {
DartCore.logError(e);
}
return null;
}
public boolean hasSdk() {
//TODO (pquitslund): add a switch to check for analysis engine enablement
return getSdk() != null && getSdk() != NONE;
}
/**
* Return {@code true} if the Dartium binary is available.
*
* @return {@code true} if the Dartium binary is available
*/
public boolean isDartiumInstalled() {
return getDartiumExecutable() != null;
}
public boolean isDefaultSdk() {
return getDefaultEditorSdkDirectory().equals(getSdk().getDirectory());
}
public void removeSdkListener(DartSdkListener listener) {
listeners.remove(listener);
}
/**
* @param monitor
*/
public IStatus upgrade(String channel, IProgressMonitor monitor) {
try {
upgradeImpl(channel, monitor);
return Status.OK_STATUS;
} catch (IOException ioe) {
return new Status(IStatus.ERROR, DartCore.PLUGIN_ID, ioe.getMessage(), ioe);
}
}
protected void notifyListeners() {
for (DartSdkListener listener : listeners) {
listener.sdkUpdated(getSdk());
}
}
private File copyNewSdk(IProgressMonitor monitor, File newSDK) throws IOException {
File currentSDK = new File(getEclipseInstallationDirectory(), "dart-sdk.zip");
DownloadUtilities.copyFile(newSDK, currentSDK, monitor);
return currentSDK;
}
/**
* Return the name of the file containing the Dartium executable.
*
* @return the name of the file containing the Dartium executable
*/
private String getDartiumBinaryName() {
if (OSUtilities.isWindows()) {
return DARTIUM_EXECUTABLE_NAME_WIN;
} else if (OSUtilities.isMac()) {
return DARTIUM_EXECUTABLE_NAME_MAC;
} else {
return DARTIUM_EXECUTABLE_NAME_LINUX;
}
}
private File getDirectoryFromPath(String path) {
if (path != null) {
path = path.trim();
if (path.length() > 0) {
return new File(path);
}
}
return null;
}
private String getSdkUrl(String channel) {
String sdkZip = NLS.bind(SDK_ZIP, getPlatformCode(), getPlatformBititude());
if (channel != null) {
return channel + sdkZip;
} else {
String url = getUpdateChannelUrl();
if (url != null) {
return url + sdkZip;
}
}
return DEFAULT_UPDATE_URL + sdkZip;
}
private File getUpdatePropertiesFile() throws IOException, URISyntaxException {
Bundle bundle = Platform.getBundle(DartCore.PLUGIN_ID);
URL url = bundle.getEntry("update.properties");
URL resolvedUrl = FileLocator.resolve(url);
// Ensure file system chars are properly escaped
// (https://bugs.eclipse.org/bugs/show_bug.cgi?id=145096)
URI fileUri = new URI(resolvedUrl.getProtocol(), resolvedUrl.getPath(), null);
return new File(fileUri);
}
/**
* Return the user-defined directory.
*
* @return the directory or {@code null} if it is not defined
*/
private File getUserDefinedDirectory(String key) {
String sdkPath = DartCore.getUserDefinedProperty(key);
return getDirectoryFromPath(sdkPath);
}
private void initSdk() {
File sdkDir = getUserDefinedDirectory(USER_DEFINED_SDK_KEY);
if (sdkDir != null && !sdkDir.exists()) {
DartCore.logError(USER_DEFINED_SDK_KEY + " defined in " + DartCore.EDITOR_PROPERTIES
+ " but does not exist: " + sdkDir);
sdkDir = null;
}
if (sdkDir == null) {
sdkDir = getDirectoryFromPath(DartCore.getPlugin().getSdkLocationPref());
if (sdkDir != null && !sdkDir.exists()) {
sdkDir = null;
}
}
if (sdkDir == null) {
sdkDir = getDefaultPluginsSdkDirectory();
if (!sdkDir.exists()) {
sdkDir = getDefaultEditorSdkDirectory();
if (!sdkDir.exists()) {
sdkDir = null;
}
}
}
if (sdkDir != null) {
sdk = new DirectoryBasedDartSdk(sdkDir);
// create an artificial context for SDK
if (DartCoreDebug.ENABLE_ANALYSIS_SERVER) {
// TODO(scheglov) restore or remove for the new API
// sdkContextId = DartCore.getAnalysisServer().createContext(
// sdkDir.getAbsolutePath(),
// sdkDir.getAbsolutePath(),
// ImmutableMap.<String, String> of());
}
} else {
sdk = NONE;
}
}
private void unzipNewSDK(File newSDK, IProgressMonitor monitor) throws IOException {
File sdkDirectory = getDefaultPluginsSdkDirectory();
if (sdkDirectory.exists()) {
DownloadUtilities.deleteDirectory(sdkDirectory);
}
DownloadUtilities.unzip(newSDK, getDefaultPluginsSdkDirectory().getParentFile(), monitor);
}
private void upgradeImpl(String channel, IProgressMonitor monitor) throws IOException {
try {
// init progress
SubMonitor mon = SubMonitor.convert(monitor, "Downloading Dart SDK", 100);
URI downloadURI = URI.create(getSdkUrl(channel));
// download to a temp file
File tempFile = DownloadUtilities.downloadZipFile(
downloadURI,
SDK_DIR_NAME,
"Download SDK",
mon.newChild(80));
// copy the new sdk
File newSdk = copyNewSdk(mon.newChild(3), tempFile);
tempFile.delete();
// unzip
unzipNewSDK(newSdk, mon.newChild(10));
// swap out the new sdk for the old
this.sdk = null;
initSdk();
// send upgrade notifications
notifyListeners();
DartCore.getConsole().printSeparator("Dart SDK update");
DartCore.getConsole().println(
"Dart SDK updated to version " + getManager().getSdk().getSdkVersion());
} finally {
monitor.done();
}
}
}