Skip to content


Support for building MSI installers on Windows #1706

wants to merge 5 commits into from

2 participants


This change adds support for building MSI installer packages for Windows, as per discussion with Ryan on Tuesday. You can check out a sample build at

Building MSIs adds one more prerequisite to the dev environment on Windows: WIX 3.5 toolset which can be downloaded from This is only required for building the MSI; building the product alone continues to work without it.

In the simplest form, the MSI can be built with the vcbuild.bat script:

vcbuild retail msi - builds retail node.exe and the MSI
vcbuild nobuild debug msi - builds MSI only using the node.exe already in the debug directory

The vcbuild script will assign the product version to the MSI based on the version number extracted from src\node_version.h. The product version number shows up in the list of installed applications.

The MSI package installs node.exe, LICENSE, and - in case of debug builds only - node.pdb to %programfiles%\nodejs directory.

The MSI displays the license and requires the user to accept it before installation.

Currently the installer project only supports installing x86 builds, but I notice this is an all-up restriction of vcbuild.bat at the moment.

Currently there is no support for side by side installation of different versions. One must uninstall previous version to install a new one.

Currently NPM is not installed as part of the package.

At the organic level, an MSI may be build using the msbuild solution at tools\msi\nodemsi.sln with the following command line:

msbuild tools\msi\nodemsi.sln /t:Clean,Build /p:Configuration=Debug /p:NodeVersion=

the tools\msi\nodemsi.wixproj can also be integrated in an all-up solution file, but I decided against it since I expect MSI building to be infrequent and not done by majority of node devs on Windows.

Let me know what you think.


ry commented

I get this error:

node.vcxproj -> C:\Users\ryan\node\Release\node.exe
C:\Users\ryan\node\tools\msi\nodemsi.wixproj(37,3): error MSB4019: The imported 
project "C:\Program Files\MSBuild\Microsoft\WiX\v3.x\Wix.targets" was not found.
Confirm that the path in the <Import> declaration is correct, and that the file exists 
on disk.

The license file should not be included unless this is intended to be distributed under something other than the MIT license. This is all original code for the Node project - correct?


When you say license should not be included you mean the installer UI or the files being installed?

This is all original code from node, and the license is taken from the root of the enlistment. If you don;t want to install the license we don't have to, but at minimum we should ask the user to accept it during the installation.

Have you installed WIX 3.5 Toolset from

ry commented

Oh okay - so the license file is for displaying during install - that's fine. Is it possible to use the plaintext LICENSE file at the root of the Node tree instad of this RTF file? (It would be difficult for us to maintain both files)

I installed WIX now. I'm getting this error:

v8_snapshot.vcxproj -> C:\Users\ryan\node\Release\v8_snapshot.lib
node.vcxproj -> C:\Users\ryan\node\Release\node.exe 
C:\Users\ryan\node\tools\msi\product.wxs(45): error LGHT0094:  
Unresolved reference to symbol 'WixComponentGroup:Product.Generated'
in section 'Product:{CC6C176E-E26C-48EC-8970-F58BD1D046CF}'. 

Regarding version number, perhaps we can use the fourth component of the product version to reflect the status, e.g. would be pre-release, would be release. Thoughts?


...or it can be the number of commits since the previous version tag, or just a rolling build number.


Unfortunately WIX only supports RTF. We could automatically generate the RTF from TXT using python as a pre-build step, the RTF format is not rocket science.

What command are you using for building? Can you try building node alone first, and then

msbuild tools\msi\nodemsi.sln /t:Clean,Build /p:Configuration=Debug /p:NodeVersion= /v:d

The logs should have more information - can you post them in gist?


you should use the same /p:Configuration as the node.exe build of course.

@ry ry closed this in 6fadbec
ry commented

Sorry that this took so long to merge. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Showing with 143 additions and 2 deletions.
  1. +1 −0 .gitignore
  2. BIN tools/msi/LICENSE.rtf
  3. +9 −0 tools/msi/
  4. +20 −0 tools/msi/nodemsi.sln
  5. +46 −0 tools/msi/nodemsi.wixproj
  6. +53 −0 tools/msi/product.wxs
  7. +14 −2 vcbuild.bat
1 .gitignore
@@ -18,6 +18,7 @@ node_g
BIN tools/msi/LICENSE.rtf
Binary file not shown.
9 tools/msi/
@@ -0,0 +1,9 @@
+import sys,re;
+for line in sys.stdin:
+ if re.match('#define NODE_MAJOR_VERSION', line):
+ major = line.split()[2]
+ if re.match('#define NODE_MINOR_VERSION', line):
+ minor = line.split()[2]
+ if re.match('#define NODE_PATCH_VERSION', line):
+ patch = line.split()[2]
+print '{0:s}.{1:s}.{2:s}.0'.format(major, minor, patch)
20 tools/msi/nodemsi.sln
@@ -0,0 +1,20 @@
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "nodemsi", "nodemsi.wixproj", "{1D808FF0-B5A9-4BE9-859D-B334B6F48BE2}"
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1D808FF0-B5A9-4BE9-859D-B334B6F48BE2}.Debug|x86.ActiveCfg = Debug|x86
+ {1D808FF0-B5A9-4BE9-859D-B334B6F48BE2}.Debug|x86.Build.0 = Debug|x86
+ {1D808FF0-B5A9-4BE9-859D-B334B6F48BE2}.Release|x86.ActiveCfg = Release|x86
+ {1D808FF0-B5A9-4BE9-859D-B334B6F48BE2}.Release|x86.Build.0 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
46 tools/msi/nodemsi.wixproj
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>3.5</ProductVersion>
+ <ProjectGuid>{1d808ff0-b5a9-4be9-859d-b334b6f48be2}</ProjectGuid>
+ <SchemaVersion>2.0</SchemaVersion>
+ <OutputName>node</OutputName>
+ <OutputType>Package</OutputType>
+ <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
+ <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
+ <NodeVersion Condition=" '$(NodeVersion)' == '' "></NodeVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <OutputPath>..\..\$(Configuration)\</OutputPath>
+ <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
+ <DefineConstants>Debug;ProductVersion=$(NodeVersion)</DefineConstants>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <OutputPath>..\..\$(Configuration)\</OutputPath>
+ <IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
+ <DefineConstants>Debug;ProductVersion=$(NodeVersion)</DefineConstants>
+ </PropertyGroup>
+ <ItemGroup>
+ <Compile Include="product.wxs" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="LICENSE.rtf" />
+ </ItemGroup>
+ <ItemGroup>
+ <WixExtension Include="WixUIExtension">
+ <HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
+ <Name>WixUIExtension</Name>
+ </WixExtension>
+ </ItemGroup>
+ <Import Project="$(WixTargetsPath)" />
+ <!--
+ To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Wix.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
53 tools/msi/product.wxs
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="">
+ <?define sourcedir="$(var.ProjectDir)..\..\$(var.Configuration)\" ?>
+ <Product Id="cc6c176e-e26c-48ec-8970-f58bd1d046cf"
+ Name="node.js"
+ Language="1033"
+ Version="$(var.ProductVersion)"
+ Manufacturer="Joyent, Inc"
+ UpgradeCode="1d60944c-b9ce-4a71-a7c0-0384eb884baa">
+ <Package InstallerVersion="200" Compressed="yes" Platform="x86" />
+ <Media Id="1" Cabinet="" EmbedCab="yes" />
+ <Directory Id="TARGETDIR" Name="SourceDir">
+ <Directory Id="ProgramFilesFolder">
+ <Directory Id="NodeRoot" Name="nodejs">
+ <Component Id="nodeexe" Guid="AEC0F08E-89B3-4C35-A286-8DB8598597F2">
+ <File Id="filenodeexe" KeyPath="yes" Source="$(var.sourcedir)\node.exe" />
+ </Component>
+ <?if $(var.Configuration) = Debug ?>
+ <Component Id="nodepdb" Guid="BEC0F08E-89B3-4C35-A286-8DB8598597F2">
+ <File Id="filenodepdb" KeyPath="yes" Source="$(var.sourcedir)\node.pdb" />
+ </Component>
+ <?endif?>
+ <Component Id="license" Guid="CEC0F08E-89B3-4C35-A286-8DB8598597F2">
+ <File Id="filelicense" KeyPath="yes" Source="$(var.sourcedir)\..\LICENSE" />
+ </Component>
+ </Directory>
+ </Directory>
+ </Directory>
+ <ComponentGroup Id="allfiles">
+ <ComponentRef Id="nodeexe"/>
+ <?if $(var.Configuration) = Debug ?>
+ <ComponentRef Id="nodepdb"/>
+ <?endif?>
+ <ComponentRef Id="license"/>
+ </ComponentGroup>
+ <Feature Id="nodejs" Title="node.js engine" Level="1" Description="evented I/O for V8 javascript">
+ <ComponentGroupRef Id="allfiles" />
+ <ComponentGroupRef Id="Product.Generated" />
+ </Feature>
+ <WixVariable Id="WixUILicenseRtf" Value="$(var.ProjectDir)\license.rtf" />
+ <UIRef Id="WixUI_Minimal" />
+ </Product>
16 vcbuild.bat
@@ -18,6 +18,7 @@ set noprojgen=
set nobuild=
set test=
set test_args=
+set msi=
if "%1"=="" goto args-done
@@ -33,6 +34,7 @@ if /i "%1"=="test-simple" set test=test-simple&goto arg-ok
if /i "%1"=="test-message" set test=test-message&goto arg-ok
if /i "%1"=="test-all" set test=test-all&goto arg-ok
if /i "%1"=="test" set test=test&goto arg-ok
+if /i "%1"=="msi" set msi=1&goto arg-ok
goto next-arg
@@ -51,7 +53,7 @@ echo Project files generated.
@rem Skip project generation if requested.
-if defined nobuild goto run
+if defined nobuild goto msi
@rem Bail out early if not running in VS build env.
if defined VCINSTALLDIR goto msbuild-found
@@ -70,6 +72,15 @@ goto run
msbuild node.sln /t:%target% /p:Configuration=%config% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
if errorlevel 1 goto exit
+@rem Skip msi generation if not requested
+if not defined msi goto run
+python "%~dp0tools\msi\" < "%~dp0src\node_version.h" > "%temp%\node_version.txt"
+if not errorlevel 0 echo Cannot determine current version of node.js & goto exit
+for /F "tokens=*" %%i in (%temp%\node_version.txt) do set NODE_VERSION=%%i
+msbuild "%~dp0tools\msi\nodemsi.sln" /t:Clean,Build /p:Configuration=%config% /p:NodeVersion=%NODE_VERSION% /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo
+if errorlevel 1 goto exit
@rem Run tests if requested.
if "%test%"=="" goto exit
@@ -94,9 +105,10 @@ echo Failed to create vc project files.
goto exit
-echo vcbuild.bat [debug/release] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [nobuild]
+echo vcbuild.bat [debug/release] [msi] [test-all/test-uv/test-internet/test-pummel/test-simple/test-message] [clean] [noprojgen] [nobuild]
echo Examples:
echo vcbuild.bat : builds debug build
+echo vcbuild.bat release msi : builds release build and MSI installer package
echo vcbuild.bat test : builds debug build and runs tests
echo vcbuild.bat release test-uv: builds release build and runs --libuv tests
goto exit
Something went wrong with that request. Please try again.