blob: 8e7af38d6e204795eab11ab674813625cd34be9c [file] [log] [blame]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/**
* This file contains functionality for getting dart version numbers using
* our standard version construction method. Systems that does not include this
* file should emulate the structure for revision numbers that we have here.
*
* The version number of a dart build is constructed as follows:
* 1. The major, minor, build and patch numbers are extracted from the VERSION
* file in the root directory. We call these MAJOR, MINOR, BUILD and PATCH.
* 2. The svn revision number for the current checkout is extracted from the
* source control system that is used in the current checkout. We call this
* REVISION.
* 3. If this is _not_ a official build, i.e., this is not build by our
* buildbot infrastructure, we extract the user-name of the logged in
* person from the operating system. We call this USERNAME.
* 4. The version number is constructed as follows:
* MAJOR.MINOR.BUILD.PATCH_rREVISION_USERNAME
*/
library version;
import "dart:async";
import "dart:io";
/**
* Generates version information for builds.
*/
class Version {
String _versionFileName;
String USERNAME;
int REVISION;
int MAJOR;
int MINOR;
int BUILD;
int PATCH;
Version(Path versionFile) {
_versionFileName = versionFile.toNativePath();
}
/**
* Get the version number for this specific build using the version info
* from the VERSION file in the root directory and the revision info
* from the source control system of the current checkout.
*/
Future<String> getVersion() {
File f = new File(_versionFileName);
Completer c = new Completer();
var wasCompletedWithError = false;
completeError(String msg) {
if (!wasCompletedWithError) {
c.completeError(msg);
wasCompletedWithError = true;
}
}
f.exists().then((existed) {
if (!existed) {
completeError("No VERSION file");
return;
}
Stream<String> stream =
f.openRead().transform(new StringDecoder())
.transform(new LineTransformer());
stream.listen((String line) {
if (line == null) {
completeError(
"VERSION input file seems to be in the wrong format");
return;
}
var values = line.split(" ");
if (values.length != 2) {
completeError(
"VERSION input file seems to be in the wrong format");
return;
}
var number = 0;
try {
number = int.parse(values[1]);
} catch (e) {
completeError("Can't parse version numbers, not an int");
return;
}
switch (values[0]) {
case "MAJOR":
MAJOR = number;
break;
case "MINOR":
MINOR = number;
break;
case "BUILD":
BUILD = number;
break;
case "PATCH":
PATCH = number;
break;
default:
completeError("Wrong format in VERSION file, line does not "
"contain one of {MAJOR, MINOR, BUILD, PATCH}");
return;
}
},
onDone: () {
// Only complete if we did not already complete with a failure.
if (!wasCompletedWithError) {
getRevision().then((revision) {
REVISION = revision;
USERNAME = getUserName();
var userNameString = "";
if (USERNAME != '') userNameString = "_$USERNAME";
var revisionString = "";
if (revision != 0) revisionString = "_r$revision";
c.complete(
"$MAJOR.$MINOR.$BUILD.$PATCH$revisionString$userNameString");
return;
});
}
});
});
return c.future;
}
String getExecutableSuffix() {
if (Platform.operatingSystem == 'windows') {
return '.bat';
}
return '';
}
int getRevisionFromSvnInfo(String info) {
if (info == null || info == '') return 0;
var lines = info.split("\n");
RegExp exp = new RegExp(r"Last Changed Rev: (\d*)");
for (var line in lines) {
if (exp.hasMatch(line)) {
String revisionString = (exp.firstMatch(line).group(1));
try {
return int.parse(revisionString);
} catch(e) {
return 0;
}
}
}
return 0;
}
Future<int> getRevision() {
if (repositoryType == RepositoryType.UNKNOWN) {
return new Future.immediate(0);
}
var isSvn = repositoryType == RepositoryType.SVN;
var command = isSvn ? "svn" : "git";
command = "$command${getExecutableSuffix()}";
var arguments = isSvn ? ["info"] : ["svn", "info"];
ProcessOptions options = new ProcessOptions();
// Run the command from the root to get the last changed revision for this
// "branch". Since we have both trunk and bleeding edge in the same
// repository and since we always build TOT we need this to get the
// right version number.
Path toolsDirectory = new Path(_versionFileName).directoryPath;
Path root = toolsDirectory.join(new Path(".."));
options.workingDirectory = root.toNativePath();
return Process.run(command, arguments, options).then((result) {
if (result.exitCode != 0) {
return 0;
}
return getRevisionFromSvnInfo(result.stdout);
});
}
bool isProductionBuild(String username) {
return username == "chrome-bot";
}
String getUserName() {
// TODO(ricow): Don't add this on the buildbot.
var key = "USER";
if (Platform.operatingSystem == 'windows') {
key = "USERNAME";
}
if (!Platform.environment.containsKey(key)) return "";
var username = Platform.environment[key];
// If this is a production build, i.e., this is something we are shipping,
// don't suffix the version with the username.
if (isProductionBuild(username)) return "";
return username;
}
RepositoryType get repositoryType {
bool isWindows = Platform.operatingSystem == 'windows';
bool hasDirectory(path, name) {
return new Directory.fromPath(path.append(name)).existsSync();
}
bool isFileSystemRoot(absolutePath) {
if (isWindows) {
return "${absolutePath.directoryPath}" == '/';
}
return "$absolutePath" == '/';
}
var currentPath = new Path(new Directory.current().path);
while (true) {
if (hasDirectory(currentPath, '.svn')) {
return RepositoryType.SVN;
} else if (hasDirectory(currentPath, '.git')) {
return RepositoryType.GIT;
}
if (isFileSystemRoot(currentPath)) {
break;
}
currentPath = currentPath.directoryPath;
}
return RepositoryType.UNKNOWN;
}
}
class RepositoryType {
static final RepositoryType SVN = const RepositoryType("SVN");
static final RepositoryType GIT = const RepositoryType("GIT");
static final RepositoryType UNKNOWN = const RepositoryType("UNKNOWN");
const RepositoryType(String this.name);
static RepositoryType guessType() {
if (new Directory(".svn").existsSync()) return RepositoryType.SVN;
if (new Directory(".git").existsSync()) return RepositoryType.GIT;
return RepositoryType.UNKNOWN;
}
String toString() => name;
final String name;
}