27 November 2013

 

Compiling PhoneGap 3.1 hello-world to Android

Installing PhoneGap 3.1 was really a breeze - just install NodeJS then npm install -g phonegap.

Getting my first PhoneGap project just to compile, on the other hand, has been a sheer headache. For other adventurers there are quite a few incongruities to beware:

Firstly, you probably won't have setup your PATH and aligned the planets correctly for the local Android SDK to be used, so a simple phonegap run android tries to use the Adobe PhoneGap Build web service for building your app. But you need to have a Build account, for which you need an AdobeID or a GitHub account. Oh, but PhoneGap won't support GitHub accounts, so you need an AdobeID. And a paid Build account if you want to build more than 1 app.

So PhoneGap Build isn't what I really wanted. I wanted to build and test locally. For free.

It turns out you can force a local build with phonegap local build android which results in

[phonegap] adding the Android platform...
   [error] The command `android` failed. Make sure you have the latest Android SDK installed, and the `android` command (inside the tools/ folder) added to your path. Output: 'android' is not recognized as an internal or external command, operable program or batch file.

Checking back with the (out-of-date) PhoneGap/Android doco, discover I need adt-bundle/sdk/platform-tools and adt-bundle/sdk/tools in my PATH.

Done.

[phonegap] adding the Android platform...
   [error] Please install Android target 17 (the Android 4.2 SDK). Make sure you have the latest Android tools installed as well. Run `android` from your command-line to install/update any missing SDKs or tools.

Oh. Well the latest ADT (20131030) I just installed has API 19 (4.4), and in fact I would prefer to target API 14 (4.0) so installed that too. Other leads suggested setting up the project with an android target: phonegap local install android

[phonegap] trying to install app onto device
[phonegap] no device was found
[phonegap] trying to install app onto emulator
   [error] No platforms added to this project. Please use `cordova platform add <platform>`.

"cordova ..."? I'm using PhoneGap! Ok, in hindsight I should have paid attention to the first line that said "trying to install app onto device" and realised that was the completely wrong command, but instead I followed the error message's advice, intelligently replacing "cordova" with "phonegap", summoning phonegap platform add android, which threw me waaay off track with this:

[error] 'platform add android' is not a node path\to\npm\node_modules\phonegap\bin\phonegap.js command. See 'node path\to\npm\node_modules\phonegap\bin\phonegap.js help'

How does one target a different API? More leads suggested I edit AndroidManifest.xml under platforms\android... a folder and file that did not exist. How to create them? phonegap build android of course! Aaaaargh!

At this point I just bit the bullet and installed API 17. Cool, so you don't even need "local" in your command now, a simple phonegap build android will suffice.

[phonegap] detecting Android SDK environment...
[phonegap] using the local environment
[phonegap] adding the Android platform...
   [error] An error occured during creation of android sub-project. ERROR : executing command 'ant', make sure you have ant installed and added to your path.

...unless ant is not installed. But it's included with ADT, so just add path\to\adt\eclipse\plugins\org.apache.ant_1.8.3.v201301120609\bin to your PATH then you're done.

[phonegap] adding the Android platform...
   [error] An error occured during creation of android sub-project. Creating Cordova project for the Android platform: Command failed to execute : ant jar

Well, not done-done. You (possibly) need Android SDK Build Tools v17 (via the Android SDK Manager) which is of course not installed by default in the latest ADT.

And you need to have the JDK in your path, not the JRE, or at least have the JDK earlier in the path order than JRE.

And you can't have any spaces in the path to your PhoneGap project folder.

And finally after all this, you can phonegap run android to compile your app and launch it in the Android emulator, hooray!

As it turns out, the AndroidManifest.xml reveals that minSdkVersion=10, so my attempt to use API 14 was not necessary. So the actual steps to getting PhoneGap to locally compile to Android are:


21 July 2009

 

The correct way to redirect standard streams in .NET

If you're wanting to redirect the standard IO streams for a command line process from C#, most google results just create a new Process() then call StandardOutput.ReadToEnd(). Unfortunately this is a simplistic and naive approach that fails to account for the buffered nature of the streams.

You see, if the process outputs significantly to both stdout and stderr, using the simplistic approach above wil lcause the stream buffer to fill with unread stderr content before all the stdout content is read. When this happens, ReadToEnd stalls, waiting for more stdout, but none will be arrive because the buffer is full of stderr, so no new stdout can be written to it.

The correct approach is to read from both stdout and stderr asynchronously, via a delegates and BeginInvoke/EndInvoke. While we're at it, it's pretty easy to provide input to stdin, completing the trilogy. What's more, it all wraps up into a nice little self-contained method:

// Interface for reading stdout and stderr of the child process.
private delegate string AsyncStringReader();

private void Run( string cmd, string args, string stdin, out string stdout, out string stderr )
{
// Redirect std streams of the child process.
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;

// Run the command, feeding the provided input.
p.StartInfo.FileName = cmd;
p.StartInfo.Arguments = args;
p.Start();
p.StandardInput.Write( stdin );
p.StandardInput.Close();

// Stdout and stderr need to be read asynchronously, otherwise their
// internal buffers fill up (<6k characters) and block, preventing
// the child process from finishing its execution.
AsyncStringReader outReader = new AsyncStringReader( p.StandardOutput.ReadToEnd );
AsyncStringReader errReader = new AsyncStringReader( p.StandardError.ReadToEnd );
IAsyncResult outTask = outReader.BeginInvoke( null, null );
IAsyncResult errTask = errReader.BeginInvoke( null, null );
p.WaitForExit(); // possibly not needed

// Return the output streams.
stdout = outReader.EndInvoke( outTask );
stderr = errReader.EndInvoke( errTask );
}

6 March 2009

 

Real men use GUIs

I understand the utility of a command line interface. I even use one on a daily basis even. But I feel comfortable in admitting that I'd much prefer an IDE or GUI any day, because it reflects[1] a basic male instinct that my sex are inherently visual creatures.

Having endured frequent derision from my fellow nerds for a lack of enthusiasm toward text-based interfaces, I'm left with no choice but to liken the command line to a saucy Harold Robbins, and the GUI to a classy gentlemen's interest magazine, from which we can deduce:
CLI: girly
GUI: manly
Ladies, the romantic ol' command line is all yours. Nerdy men, don't deny your chromosomes!


[1] It also acknowledges that certain advancements have been made in the field of computing over the last 30 years.

19 July 2008

 

Custom Tools for Visual Studio

Possibly the coolest thing I've done so far as a programmer is to produce a Visual Studio CustomTool (think plugin) that allows source files from a custom language to be compiled into a C# project. The need for such a beast arose when i was porting an old database system, from a platform called CRS (think MS Access, but older and much more powerful) to C#. There were 100k+ lines of code that needed converting, so an automated tool seemed faster and saner than doing it all by hand.

The converter was written in Python courtesy of the pyparsing module, and i initially went about trying to find a way to automatically run it from the Visual Studio build process. Hooking into the MSBuild process didn't seem all that appealing, as it required messing around with the build (project) files, or creating a butt-load of .map files, as far as i could gather. At some stage during my googling, I stumbled across this mysterious CustomTool concept, which promised not just CRS code files compiled inside my C# project, but also a technique that would work across all Visual Studio editions (including express, so i could work from home).

Custom tools work by generating a module based on some arbitrary source file. The module is regenerated each time the source file changes. You can see custom tools in action with strongly typed data sets, and resource files... they generate the .Designer.cs files you see in the Solution Explorer tree.

Apparently creating a custom tool is easy, and it sure would be if there was actually any thorough and coherent documentation on the process. I've struggled through many narrow articles, broken links and sample code that could have been documented a lot better, all tied together with some heavy googling. I'm hoping this article will provide a little more breadth on the topic.

To get your custom tool working, there are only a few things you need to do:
  1. Produce a dll that implements IVsSingleFileGenerator
  2. Register the dll for COM
  3. Register the dll with Visual Studio
  4. Configure your source files to use the dll
...but each step has a multiple ways of being accomplished, and discovering what the options are, let alone deducing which is most suitable can be quite difficult, given the prevailing documentation.

Implement IVsSingleFileGenerator

Implementing IVsSingleFileGenerator interface should be easy, since there are only 2 methods in it. Of course the MSDN reference is typically scant and not much help on its own. And if you want to read it from your local MSDN installation, you have to first install the Visual Studio SDK for your version of Visual Studio. In fact it's a good idea to install this, as it has a sample custom tool project with some useful wrapper classes.

From the SDK's "SingleFileGenerator" sample project, copy BaseCodeGenerator.cs into your tool project and inherit from it. It converts the IVsSingleFileGenerator interface into something much simpler to implement. You could probably figure out how do write your own BaseCodeGenerator equivalent, but MS has already done it for you, so why reinvent the wheel? Many articles on the net will tell you that you should derive your tool class from BaseCodeGeneratorWithSite.cs, but unless you need the extra features it reveals, there's no need. BaseCodeGenerator requires your tool project to reference Microsoft.VisualStudio.Shell and Microsoft.VisualStudio.Shell.Interop. BaseCodeGeneratorWithSite requires a heap more references, good luck with that.

As a side note, there seems to be a bit of history to this BaseCodeGenerator/WithSite class set that makes me think Microsoft doesn't want us writing our own custom tools. Apparently it was included in the .NET 1.1 libraries, but made internal for 2.0+. At the time it was made internal, Microsoft released the code for it to the GotDotNet website, but later obliterated when they replaced the whole site with CodePlex late in 2007. Most articles I came across still reference the GotDotNet version, which now redirects to a lame CodePlex apology page. So now the only place to get these classes seems to be the SDK... makes sense, but what a run-around it's been hunting it down.

Register for COM

Now, merely implementing the interface is not enough. Because custom tools operate through COM, we need to make our tool play nicely with COM. Firstly, your class must have it's own GUID, which can be generated with the uuidgen command line tool that comes with Visual Studio, or from the IDE's Tools menu, neither of which seem to be available in the Express edition of C#. The GUID is associated with your tool class via an attribute, eg:
[Guid( "EB35D84B-279D-4BAC-B587-8BE5FB28D889" )]
public class CrsCodeGenerator : BaseCodeGenerator
{
Then you need to make your class "COM visible" by checking "Make assembly COM-visible" under project properties | Application | Assembly information.

Once you have compiled your tool into a dll, there are a ton of ways to register your custom tool for COM. The easiest way is to have Visual Studio do it for you during the dll compilation: check "Register fo COM interop" under project properties | Build. This is equivalent to running "regasm /codebase", which means the dll doesn't have to be in a pathed directory, but if you later move or delete the dll, Visual Studio won't be able to find it.

Alternatively, you could copy your dll into the global assembly cache (GAC), either with gacutil or just copying into c:\windows\assembly. But to do that, your dll assembly needs a "strong name", ie it needs to be signed. Check "Sign the assembly" under project properties | Signing, then generate a new key file from the dropdown box underneath.

But I didn't do any of these (except for signing the assembly - now I can't remember if I actually needed to). Instead, I generated the registry entries required for COM registration:
regasm CrsCodeGenerator.dll /regfile
and copied the output into a reg file that i then included in the project. This reg file is imported during build by including the following lines in my post-build event command line:
regedit.exe /s "$(ProjectDir)CrsCodeGenerator.reg"
copy "$(TargetPath)" "$(DevEnvDir)"
This doesn't require any unusual path environment assumptions... regedit will pretty much always be in a pathed directory. The copy command copies the compiled dll into Visual Stuido's home folder, so there is no need to mess with the GAC.

The reason I used a regfile instead of just calling regasm in the post-built event is because regasm is not in a nicely-defined folder that works across all Visual Studio editions. It doesn't even come with 2005 Express.

Register with Visual Studio

This needs a few registry entries:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Generators\{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}\]
@=""
"CLSID"="{}"
"GeneratesDesignTimeSource"=dword:00000001
You've probably guessed this only registers for Visual Studio 8.0 (2005). Adjust they key if you want to use your tool with other versions of VIsual Studio. The GUID in path corresponds to C# projects. A different GUID is required if you want your tool to work with VB projects, go look it up! The tool name, I suspect, needs to be short with no spaces, but I haven't tried otherwise. I used "CrsCustomTool". When this text is entered into the CustomTool property of a project file, our tool is run on that file. I'm not sure what the requirements are on the short title, I just put "CRS to C# Code Generator", it doesn't seem to be used anywhere I could see. The GUID is taken from the Guid attribute you generated for your tool class.

Once again I put thes registry entries in my project reg file, which is merged in the post-build event discussed above.

Actually, Daniel Cazzulino describes a really nice way to have these registry entries generated by regasm. I haven't done this, but it might make it worthwhile using regasm or the "Register for COM Interop" property, and ditching the reg file. Yeat another option, sigh.

Use it!

If you've set your tool project up like me, all you have to do to "install" it is compile the project. Enter the tool name in a source file's CustomTool property and watch the magic happen!

Other Bits

Your tool can generate warning and error messages that appear in the Error List, and even goto the source when double clicked. Call GeneratorError(0,msg,line-1,column-1) or GenerateWarning(...). Note that if either of line/column aren't valid, the double click feature won't work. Note these parameters are 0-based indexes, but are displayed (correctly) as 1-based index in the Error List.

You can even automatically associate your custom tool with a particular file extension.

Summary

So there you go, a cenceptually-simple process, made painful by the combinatorial explosion of options at each step of the journey, and documentation scattered around the tubes in blogs like this :)

My focus has been compile-to-install (then ditch the source project if need be), which is why I've chosen to do some step differently to others. But hopefully by now you'll have a better understanding of all the options, and can make an informed choice as to how you construct and deploy your next custom tool.

10 August 2007

 

Your Favourite Programmer

As a serious developer, you should have at least one favourite coder, your own programming hero. Each of us can always serve to learn a little more, so there's no reason why age or experience should preclude you from having someone to look up to. Your hero doesn't even have to be anyone famous - there's plenty to be learnt from your colleagues too. In no particular order, I present mine:

Peter Norvig
Check out his site, in particular his commentary/instructional Teach Yourself Progamming in Ten Years. He has released some breathtakingly beautiful python code for one of his AI books.

Scott Meyers
If only for his book Effective C++, which should be compulsory reading for anyone who writes even a single line of C++ in their lifetime.

Michael Kennett
A former colleague and informal mentor, who guided me from out the darkness of bedroom code monkeyism, toward the clouds of coding virtue.

If you have no idea who your favourite programmer is, you could either copyreuse mine, or read up on a few of your own. To give you a head start, here are some other programming legends:
Even software giants have their own heros!

7 August 2007

 

/* Comments Please */

Comments in code. Comments on checkins. Every programming language and source control tool supports comments. There must be thousands of articles on the web about how to do them properly, and as many more on how not to.

Every developer should already be using comments in their work. Descriptions on the algorithm chosen, what other algorithms were considered, usage notes for a custom class, a method's pre- and post-conditions, every single change that was made for a particular revision. If you are not already doing these, then this article is not for you. Neither is a career in software.

So why is poor/lack-of commenting still an issue in this millennium? Why don't your inline comments help me understand your code? Why can't I easily spot the revision you made a particular change? Why have you not written any comment at all?!!

Only high voltage electrodes can help answer that last one, but for the rest, the answer is why. Why is what's needed in comments. There is a lot of what in comments, but rarely a why. The what can often be induced by code inspection, but to make me induce the why is to ask me to read your mind. Whenever you are writing an inline comment or checkin comment, always ask yourself why did I do it this way, and a useful comment will result.

Implicitly, why also includes why not. Why were other approaches not taken? These are important too, as they will prevent another developer from replacing your carefully selected and beautifully crafted solution with something less appropriate you'd already deliberated over and ruled out.

Don't be surprised if you find it tough to write concise comments. It's basically summarising your entire thought process over the last hour/day/week into a few sentences. Don't be afraid to write a couple of short paragraphs if necessary. Comment well, and your co-workers will be grateful for it, and just maybe they won't form a plot on your life.

This page is powered by Blogger. Isn't yours?