diff --git a/package/chocolatey/[Content_Types].xml b/package/chocolatey/[Content_Types].xml
new file mode 100644
index 00000000..bf066091
--- /dev/null
+++ b/package/chocolatey/[Content_Types].xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/package/chocolatey/chocolateyInstall.ps1 b/package/chocolatey/chocolateyInstall.ps1
new file mode 100644
index 00000000..70c104e0
--- /dev/null
+++ b/package/chocolatey/chocolateyInstall.ps1
@@ -0,0 +1 @@
+Generate-BinFile "sass" "$packageFolder\tools\sass.bat"
diff --git a/package/chocolatey/chocolateyUninstall.ps1 b/package/chocolatey/chocolateyUninstall.ps1
new file mode 100644
index 00000000..ba163c6a
--- /dev/null
+++ b/package/chocolatey/chocolateyUninstall.ps1
@@ -0,0 +1 @@
+Remove-BinFile "sass" "$packageFolder\tools\sass.bat"
diff --git a/package/chocolatey/rels.xml b/package/chocolatey/rels.xml
new file mode 100644
index 00000000..f252fe63
--- /dev/null
+++ b/package/chocolatey/rels.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/package/chocolatey/sass.bat b/package/chocolatey/sass.bat
new file mode 100644
index 00000000..954937b0
--- /dev/null
+++ b/package/chocolatey/sass.bat
@@ -0,0 +1,8 @@
+@echo off
+REM Copyright 2017 Google Inc. Use of this source code is governed by an
+REM MIT-style license that can be found in the LICENSE file or at
+REM https://opensource.org/licenses/MIT.
+
+set SCRIPTPATH=%~dp0
+set arguments=%*
+dart.exe "-Dversion=SASS_VERSION" "%SCRIPTPATH%\sass.dart.snapshot" %arguments%
diff --git a/pubspec.yaml b/pubspec.yaml
index 5ceced3a..9dc88edc 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -29,4 +29,5 @@ dev_dependencies:
node_preamble: "^1.1.0"
scheduled_test: "^0.12.8"
test: "^0.12.5"
+ xml: "^2.4.0"
yaml: "^2.0.0"
diff --git a/tool/grind.dart b/tool/grind.dart
index be0399ef..3900b2f7 100644
--- a/tool/grind.dart
+++ b/tool/grind.dart
@@ -11,6 +11,7 @@ import 'package:grinder/grinder.dart';
import 'package:http/http.dart' as http;
import 'package:node_preamble/preamble.dart' as preamble;
import 'package:path/path.dart' as p;
+import 'package:xml/xml.dart' as xml;
import 'package:yaml/yaml.dart';
/// The version of Dart Sass.
@@ -83,6 +84,115 @@ npm_package() {
copy(new File('README.md'), dir);
}
+@Task('Build a Chocolatey package.')
+@Depends(snapshot)
+chocolatey_package() {
+ _ensureBuild();
+
+ var nuspec = _nuspec();
+ var archive = new Archive()
+ ..addFile(_fileFromString("sass.nuspec", nuspec.toString()))
+ ..addFile(
+ _file("[Content_Types].xml", "package/chocolatey/[Content_Types].xml"))
+ ..addFile(_file("_rels/.rels", "package/chocolatey/rels.xml"))
+ ..addFile(_fileFromString(
+ "package/services/metadata/core-properties/properties.psmdcp",
+ _nupkgProperties(nuspec)))
+ ..addFile(_file("tools/LICENSE", "LICENSE"))
+ ..addFile(_file("tools/sass.dart.snapshot", "build/sass.dart.snapshot"))
+ ..addFile(_file("tools/chocolateyInstall.ps1",
+ "package/chocolatey/chocolateyInstall.ps1"))
+ ..addFile(_file("tools/chocolateyUninstall.ps1",
+ "package/chocolatey/chocolateyUninstall.ps1"))
+ ..addFile(_fileFromString("tools/sass.bat",
+ _readAndReplaceVersion("package/chocolatey/sass.bat")));
+
+ var output = "build/sass.${_chocolateyVersion()}.nupkg";
+ log("Creating $output...");
+ new File(output).writeAsBytesSync(new ZipEncoder().encode(archive));
+}
+
+/// Creates a `sass.nuspec` file's contents.
+xml.XmlDocument _nuspec() {
+ var builder = new xml.XmlBuilder();
+ builder.processing("xml", 'version="1.0"');
+ builder.element("package", nest: () {
+ builder
+ .namespace("http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd");
+ builder.element("metadata", nest: () {
+ builder.element("id", nest: "sass");
+ builder.element("title", nest: "Sass");
+ builder.element("version", nest: _chocolateyVersion());
+ builder.element("authors", nest: "Natalie Weizenbaum");
+ builder.element("owners", nest: "nex3");
+ builder.element("projectUrl", nest: "https://github.com/sass/dart-sass");
+ builder.element("licenseUrl",
+ nest: "https://github.com/sass/dart-sass/blob/$_version/LICENSE");
+ builder.element("iconUrl",
+ nest: "https://cdn.rawgit.com/sass/sass-site/"
+ "f99ee33e4f688e244c7a5902c59d61f78daccc55/source/assets/img/"
+ "logos/logo-seal.png");
+ builder.element("bugTrackerUrl",
+ nest: "https://github.com/sass/dart-sass/issues");
+ builder.element("description",
+ nest: """
+**Sass makes CSS fun again**. Sass is an extension of CSS, adding nested rules, variables, mixins, selector inheritance, and more. It's translated to well-formatted, standard CSS using the command line tool or a web-framework plugin.
+
+This package is Dart Sass, the new Dart implementation of Sass.
+""");
+ builder.element("summary", nest: "Sass makes CSS fun again.");
+ builder.element("tags", nest: "css preprocessor style sass");
+ builder.element("copyright",
+ nest: "Copyright ${new DateTime.now().year} Google, Inc.");
+ builder.element("dependencies", nest: () {
+ builder.element("dependency", attributes: {
+ "id": "dart-sdk",
+ // Unfortunately we need the exact same Dart version as we built with,
+ // since we ship a snapshot which isn't cross-version compatible. Once
+ // we switch to native compilation this won't be an issue.
+ "version": "[$_dartVersion]",
+ });
+ });
+ });
+ });
+
+ return builder.build() as xml.XmlDocument;
+}
+
+/// The current Sass version, formatted for Chocolatey which doesn't allow dots
+/// in prerelease versions.
+String _chocolateyVersion() {
+ var components = _version.split("-");
+ if (components.length == 1) return components.first;
+ assert(components.length == 2);
+ return "${components.first}-${components.last.replaceAll('.', '')}";
+}
+
+/// Returns the contents of the `properties.psmdcp` file, computed from the
+/// nuspec's XML.
+String _nupkgProperties(xml.XmlDocument nuspec) {
+ var builder = new xml.XmlBuilder();
+ builder.processing("xml", 'version="1.0"');
+ builder.element("coreProperties", nest: () {
+ builder.namespace(
+ "http://schemas.openxmlformats.org/package/2006/metadata/core-properties");
+ builder.namespace("http://purl.org/dc/elements/1.1/", "dc");
+ builder.element("dc:creator",
+ nest: nuspec.findAllElements("authors").first.text);
+ builder.element("dc:description",
+ nest: nuspec.findAllElements("description").first.text);
+ builder.element("dc:identifier",
+ nest: nuspec.findAllElements("id").first.text);
+ builder.element("version",
+ nest: nuspec.findAllElements("version").first.text);
+ builder.element("keywords",
+ nest: nuspec.findAllElements("tags").first.text);
+ builder.element("dc:title",
+ nest: nuspec.findAllElements("title").first.text);
+ });
+ return builder.build().toString();
+}
+
/// Ensure that the `build/` directory exists.
void _ensureBuild() {
new Directory('build').createSync(recursive: true);
@@ -108,17 +218,19 @@ Future _buildPackage(http.Client client, String os, String architecture) async {
? file.name.endsWith("/bin/dart.exe")
: file.name.endsWith("/bin/dart"));
var executable = dartExecutable.content;
- var snapshot = new File('build/sass.dart.snapshot').readAsBytesSync();
- var sassLicense = new File('LICENSE').readAsBytesSync();
- var dartLicense = new File(p.join(_sdkDir, 'LICENSE')).readAsBytesSync();
var archive = new Archive()
- ..addFile(_file(
+ ..addFile(_fileFromBytes(
"dart-sass/src/dart${os == 'windows' ? '.exe' : ''}", executable,
executable: true))
- ..addFile(_file("dart-sass/src/DART_LICENSE", dartLicense))
- ..addFile(_file("dart-sass/src/sass.dart.snapshot", snapshot))
- ..addFile(_file("dart-sass/src/SASS_LICENSE", sassLicense))
- ..addFile(_scriptFor(os));
+ ..addFile(_file("dart-sass/src/DART_LICENSE", p.join(_sdkDir, 'LICENSE')))
+ ..addFile(
+ _file("dart-sass/src/sass.dart.snapshot", "build/sass.dart.snapshot"))
+ ..addFile(_file("dart-sass/src/SASS_LICENSE", "LICENSE"))
+ ..addFile(_fileFromString(
+ "dart-sass/dart-sass${os == 'windows' ? '.bat' : ''}",
+ _readAndReplaceVersion(
+ "package/dart-sass.${os == 'windows' ? 'bat' : 'sh'}"),
+ executable: true));
var prefix = 'build/dart-sass-$_version-$os-$architecture';
if (os == 'windows') {
@@ -133,20 +245,31 @@ Future _buildPackage(http.Client client, String os, String architecture) async {
}
}
-/// Returns a shell script archive file for the given [os].
-ArchiveFile _scriptFor(String os) {
- var contents = new File("package/dart-sass.${os == 'windows' ? 'bat' : 'sh'}")
- .readAsStringSync()
- .replaceAll("SASS_VERSION", _version);
- var bytes = UTF8.encode(contents);
- return _file("dart-sass/dart-sass${os == 'windows' ? '.bat' : ''}", bytes,
- executable: true);
-}
+/// Reads [file], replaces all instances of SASS_VERSION with the actual
+/// version, and returns its contents.
+String _readAndReplaceVersion(String file) =>
+ new File(file).readAsStringSync().replaceAll("SASS_VERSION", _version);
/// Creates an [ArchiveFile] with the given [path] and [data].
///
/// If [executable] is `true`, this marks the file as executable.
-ArchiveFile _file(String path, List data, {bool executable: false}) =>
+ArchiveFile _fileFromBytes(String path, List data,
+ {bool executable: false}) =>
new ArchiveFile(path, data.length, data)
..mode = executable ? 495 : 428
..lastModTime = new DateTime.now().millisecondsSinceEpoch ~/ 1000;
+
+/// Creates a UTF-8-encoded [ArchiveFile] with the given [path] and [contents].
+///
+/// If [executable] is `true`, this marks the file as executable.
+ArchiveFile _fileFromString(String path, String contents,
+ {bool executable: false}) =>
+ _fileFromBytes(path, UTF8.encode(contents), executable: executable);
+
+/// Creates an [ArchiveFile] at the archive path [target] from the local file at
+/// [source].
+///
+/// If [executable] is `true`, this marks the file as executable.
+ArchiveFile _file(String target, String source, {bool executable: false}) =>
+ _fileFromBytes(target, new File(source).readAsBytesSync(),
+ executable: executable);