Skip to content

Commit

Permalink
Moved Task Runner Explorer Binding hook to a separate section
Browse files Browse the repository at this point in the history
  • Loading branch information
Chuxel committed Jul 15, 2015
1 parent 9c72120 commit 988f9b2
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 134 deletions.
137 changes: 3 additions & 134 deletions tutorial-gulp/gulp-task-runner-explorer.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,141 +63,10 @@ Now to attach this to the "Before Build" event, right click and select Bindings

The next time you run a build this task will automatically fire!

###Supporting the Gulp Task Runner Explorer from Command Line Builds
If you want to be able to use the "Before Build" and "After Build" event bindings **outside** of Visual Studio (say from the Cordova CLI itself) you may install the sample [Visual Studio Tools for Apache Cordova CLI Support Plugin](http://go.microsoft.com/fwlink/?LinkID=533753). This plugin works by wiring the "Before Build" event to "Before Prepare" in Cordova and "After Build" to "After Compile" when building outside of Visual Studio. **Note that currently this plugin only supports bindings in gulpfile.js in the root of your project.**

1. Open your project in Visual Studio
2. Double click on config.xml in your project
3. Select the "Plugins" tab
4. Select "Custom" and choose "Git"
5. Enter the following URI: https://github.com/Chuxel/taco-cordova-support-plugin.git
6. Click "Add"

See the [plugin Git repository](http://go.microsoft.com/fwlink/?LinkID=533753) for additional information. We will cover a simplified version of what this plugin does behind the scenes in the next section.

###Behind the Scenes: Gulp Task Cordova Hook
If you are looking for a quick way to add in support for firing Grunt tasks for Cordova build events outside of Visual Studio, consider using the sample [Visual Studio Tools for Apache Cordova CLI Support Plugin](http://go.microsoft.com/fwlink/?LinkID=533753). However, if you would prefer not to tie the implementation to the binding format used by the VS Task Runner Explorer, you can easily wire in your own Cordova hook using a similar technique.

Hooks can be implemented in a number of different ways: shell scripts, Node.js scripts, or Node modules. Fortunately, the code to call a Gulp task from a hook is trivial. In this example we'll use a Node.js module since it can run on both Windows and OSX and has less overhead than starting up a shell script.

1. Run the following commands in the command line from the root of your project. We'll automate this away next.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
npm install gulp
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2. Create a file called "hook-gulp.js" in a new "hooks" folder in your Cordova project (or any other location you would prefer)
3. Place the following code in it:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
module.exports = function (context) {
var Q = context.requireCordovaModule("q"),
fork = require("child_process").fork;
// Use the gulp node module to execute the task and return a promise
var deferred = Q.defer();
var child = fork("./node_modules/gulp/bin/gulp.js", context.hook);
child.on("error", function (err) {
deferred.reject(err);
});
child.on("exit", function (code, signal) {
if (code === 0 && signal === null) {
deferred.resolve();
} else {
deferred.reject("Non-zero exit code or signal.");
}
});
return deferred.promise;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This code simply looks at the "context" object that is passed into Cordova hook modules for the name of the hook event currently firing and then invokes a Gulp task of that same name via by "[forking](http://go.microsoft.com/fwlink/?LinkID=533804)" the Gulp node module. Since this is done asynchronously, a promise is returned to Cordova.
4. Add the following XML element to config.xml in your Cordova project. In Visual Studio you can do this using Right-Click \> View Code.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<hook type="before_prepare" src="hooks/hook-gulp.js" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can wire Gulp tasks to any number of valid Cordova [hook events](http://go.microsoft.com/fwlink/?LinkID=533744) by simply adding additional hook elements to config.xml and replacing "before\_prepare" with the event you want to wire into.
5. Create a file called "gulpfile.js" and put it in the root of your Cordova project with the following in it:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var gulp = require("gulp");
gulp.task("before_prepare", function() {
// Add anything you want to do before the build here
});
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As with step 4, you can add a Gulp task for each hook event you want to wire into Gulp.
6. Now run a Cordova build and try it out!
#### Automating Dependency Installation for our Task Runner Cordova Hook
At this point, anything you add to the "before\_prepare" Gulp task will be fired off on each build. It is really common to want to use other node modules in a Gulp task particularly in the form of plugins. You can install these manually, but let's take the next step and automate installation of Gulp and any other node modules you may want to use.
1. Create a "package.json" file in the root of your project and place the following in it:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
"devDependencies": {
"gulp": "latest"
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2. Update "hook-gulp.js" as follows:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
module.exports = function (context) {
var Q = context.requireCordovaModule("q"),
fs = require("fs"),
path = require("path"),
fork = require("child_process").fork,
exec = Q.nfbind(require("child_process").exec);
// Check to see if Gulp is installed and if not use "npm install" to
// install the contents of package.json in the node_modules folder
if (!fs.existsSync(path.join(process.cwd(), "node_modules", "gulp"))) {
console.log("Gulp not found. Installing package dependencies.")
return exec("npm install").then(function (result) {
console.log(result[0]);
if (result[1] != "") {
console.error(result[1]);
}
})
.then(function () { return runGulpTask(context.hook); });
} else {
return runGulpTask(context.hook);
}
function runGulpTask(hook) {
// Use the gulp node module to execute the task and return a promise
var deferred = Q.defer();
var child = fork("./node_modules/gulp/bin/gulp.js", hook);
child.on("error", function (err) {
deferred.reject(err);
});
child.on("exit", function (code, signal) {
if (code === 0 && signal === null) {
deferred.resolve();
} else {
deferred.reject("Non-zero exit code or signal.");
}
});
return deferred.promise;
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This will then first check to see if Gulp is already installed and if not install everything you have in package.json automatically by executing the "npm install" command from the root of your project. Either way it calls the task matching the Cordova hook event.
3. Now run a Cordova build and try it out!
#### Adding Other Dependencies
Adding additional dependencies is simple from Visual Studio. VS will also **automaticlly** install any package added to the "devDependencies" list in package.json when you save the file.
You can also install any additional dependencies and update package.json using the "--save" flag when calling "npm install" from the command line. For example, this command will add the [uglify Gulp plugin](http://go.microsoft.com/fwlink/?LinkID=533793) as a dependency:
##Supporting Task Runner Explorer Bindings from the Command Line
By default, bindings in the Task Runner Explorer only work inside of Visual Studio. When working outside of Visual Studio we generally recommend simply running the Gulp tasks directly from the command line. However, you may want to be able to simply assign bindings in Visual Studio and have them apply from builds at the command line or in a team / Continuous Integration (CI) environment. Fortunately this is fairly straight forward to do via a [Cordova "hook"](http://go.microsoft.com/fwlink/?LinkID=533744).

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
npm install --save-dev gulp-uglify
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To do so, **[follow these directions to add a pre-built Cordova hook to your project](./hook-task-runner-binding)**. You can then modify it as you see fit to meet your needs.

## More Information
* [Learn more about using Gulp with your Cordova projects](README.md)
Expand Down
7 changes: 7 additions & 0 deletions tutorial-gulp/hook-task-runner-binding/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Copyright (c) Microsoft Corporation

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
74 changes: 74 additions & 0 deletions tutorial-gulp/hook-task-runner-binding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#Cordova Hook to Execute VS Task Runner Explorer Bindings from the Command Line

License: MIT

Use this hook if you want to be able to use the "Before Build" and "After Build" event bindings **outside** of Visual Studio (say from the Cordova CLI itself or during a CI build). It wires in the "Before Build" binding in the Task Runner Explorer to "Before Prepare" in Cordova and "After Build" in the Task Runner Explorer to "After Compile" in Cordova. **Note that currently this hook only supports bindings in gulpfile.js in the root of your project.**

To install it:

1. Grab the js file and drop it a "hooks" folder in your project root
2. Update config.xml with the following:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<hook type="before_prepare" src="hooks/hook-task-runner-binding.js" />
<hook type="after_prepare" src="hooks/hook-task-runner-binding.js" />
<hook type="before_compile" src="hooks/hook-task-runner-binding.js" />
<hook type="after_compile" src="hooks/hook-task-runner-binding.js" />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

##Using the Visual Studio 2015 Task Runner Explorer
The Visual Studio Task Runner Explorer allows users to tie Gulp tasks to specific build events. To use the Task Runner Explorer:

1. Go to View > Other Windows > Task Runner Explorer
2. You will now see any gulpfiles you have in your project. (Hit the refresh icon if you added one after opening the window.)
3. To bind a task to an event, right click on the task, go to Bindings, and select the event you want

**Note: The hook currently only works if "gulpfile.js" is in the root of the project. In addition, Clean and ProjectOpened do not have direct analogs in the Cordova CLI and are currently ignored.**

This results in a comment being added to gulpfile.js with the bindings. Ex:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// <binding BeforeBuild='before-build-gulp-task' AfterBuild='after-build-gulp-task' Clean='after-build-gulp-task' ProjectOpened='before-build-gulp-task' />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The config.xml elements above will map the BeforeBuild task into the Cordova CLI before\_prepare event and the AfterBuild task to the Cordova CLI after\_compile event. These same events are fired by the cordova "build" command which combines "prepare" and "compile" in one comand while still allowing each of these commands to function separatley.

While not supported via Visual Studio UI, you can wire in Gulp tasks to additiona Cordova hooks by simply using the hook name using the syntax above. Ex:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// <binding after_prepare='after-prepare-gulp-task' />
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The hook also automatically detects if Gulp is not installed in the node_modules folder in your project and will "npm install" the contents of package.json in your project to further streamline development.

## Terms of Use
By downloading and running this project, you agree to the license terms of the third party application software, Microsoft products, and components to be installed.

The third party software and products are provided to you by third parties. You are responsible for reading and accepting the relevant license terms for all software that will be installed. Microsoft grants you no rights to third party software.


## License
Unless otherwise mentioned, the code samples are released under the MIT license.

```
The MIT License (MIT)
Copyright (c) Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

94 changes: 94 additions & 0 deletions tutorial-gulp/hook-task-runner-binding/hook-task-runner-binding.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
Copyright (c) Microsoft. All rights reserved.
Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/

var exec, fork, fs, path, Q, hookTasks;

module.exports = function (context) {

// Skip processing if being called from within Visual Studio
if (process.env["VisualStudioEdition"]) {
return;
}

fs = require('fs');
path = require('path');
Q = context.requireCordovaModule('q');
exec = Q.nfbind(require('child_process').exec);
fork = require('child_process').fork;

// Syntax:
/// <binding BeforeBuild='before-build' AfterBuild='after-build' Clean='after-build' />

// Check gulpfile.js for tasks to run for this hook
if (fs.existsSync(path.join(process.cwd(), "gulpfile.js"))) {
// Hooktasks should be cached after first execution since its at the module level
if (!hookTasks) {
console.log("Checking gulpfile for tasks to run for Cordova events.")
hookTasks = {};
var gulpfile = fs.readFileSync("gulpfile.js", "utf8");
var matches = gulpfile.match(/\/\/\/\s<binding(.+)(?=\/>)/ig);
if (matches) {
var bindings = matches[0].split(" ");
bindings.forEach(function (binding) {
binding = binding.replace(/["|']/g, "");
var bindParts = binding.split("=")
var hook = bindParts[0];
var task = bindParts[1];
if (hook.indexOf("_") == -1) {
switch (hook) {
case "BeforeBuild": hook = "before_prepare"; break;
case "AfterBuild": hook = "after_compile"; break;
case "Clean": return; // No cordova clean event
case "ProjectOpened": return; // No cordova project open event
}
} else {
hook = hook.toLowerCase();
}
if (hookTasks[hook]) {
hookTasks[hook].push(task);
} else {
hookTasks[hook] = [task];
}
});
}
}

if (hookTasks[context.hook]) {
// Install dependencies in package.json if gulp not present - Run the task either way
if (!fs.existsSync(path.join(process.cwd(), "node_modules", "gulp"))) {
console.log("Gulp not found. Installing package dependencies.")
return exec("npm install")
.then(function (result) {
console.log(result[0]);
if (result[1] != "") {
console.error(result[1]);
};
})
.then(function () { return runGulpTask(context.hook); });
} else {
return runGulpTask(context.hook);
}
}
}

function runGulpTask(hook) {
var deferred = Q.defer();
console.log("Running gulp task " + hookTasks[hook] + " for Cordova event " + hook);
var child = fork("./node_modules/gulp/bin/gulp.js", hookTasks[hook]);
child.on("error", function (err) {
deferred.reject(err);
});
child.on("exit", function (code, signal) {
if (code === 0 && signal === null) {
deferred.resolve();
} else {
deferred.reject("Non-zero exit code or signal. Code: " + code + ", Signal: " + signal);
}
});
return deferred.promise;
}

};

0 comments on commit 988f9b2

Please sign in to comment.