1// On windows, create a .cmd file. 2// Read the #! in the file to see what it uses. The vast majority 3// of the time, this will be either: 4// "#!/usr/bin/env <prog> <args...>" 5// or: 6// "#!<prog> <args...>" 7// 8// Write a binroot/pkg.bin + ".cmd" file that has this line in it: 9// @<prog> <args...> %dp0%<target> %* 10 11module.exports = cmdShim 12cmdShim.ifExists = cmdShimIfExists 13 14var fs = require("graceful-fs") 15 16var mkdir = require("mkdirp") 17 , path = require("path") 18 , toBatchSyntax = require("./lib/to-batch-syntax") 19 , shebangExpr = /^#\!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+=[^ \t]+\s+)*\s*([^ \t]+)(.*)$/ 20 21function cmdShimIfExists (from, to, cb) { 22 fs.stat(from, function (er) { 23 if (er) return cb() 24 cmdShim(from, to, cb) 25 }) 26} 27 28// Try to unlink, but ignore errors. 29// Any problems will surface later. 30function rm (path, cb) { 31 fs.unlink(path, function(er) { 32 cb() 33 }) 34} 35 36function cmdShim (from, to, cb) { 37 fs.stat(from, function (er, stat) { 38 if (er) 39 return cb(er) 40 41 cmdShim_(from, to, cb) 42 }) 43} 44 45function cmdShim_ (from, to, cb) { 46 var then = times(3, next, cb) 47 rm(to, then) 48 rm(to + ".cmd", then) 49 rm(to + ".ps1", then) 50 51 function next(er) { 52 writeShim(from, to, cb) 53 } 54} 55 56function writeShim (from, to, cb) { 57 // make a cmd file and a sh script 58 // First, check if the bin is a #! of some sort. 59 // If not, then assume it's something that'll be compiled, or some other 60 // sort of script, and just call it directly. 61 mkdir(path.dirname(to), function (er) { 62 if (er) 63 return cb(er) 64 fs.readFile(from, "utf8", function (er, data) { 65 if (er) return writeShim_(from, to, null, null, null, cb) 66 var firstLine = data.trim().split(/\r*\n/)[0] 67 , shebang = firstLine.match(shebangExpr) 68 if (!shebang) return writeShim_(from, to, null, null, null, cb) 69 var vars = shebang[1] || "" 70 , prog = shebang[2] 71 , args = shebang[3] || "" 72 return writeShim_(from, to, prog, args, vars, cb) 73 }) 74 }) 75} 76 77 78function writeShim_ (from, to, prog, args, variables, cb) { 79 var shTarget = path.relative(path.dirname(to), from) 80 , target = shTarget.split("/").join("\\") 81 , longProg 82 , shProg = prog && prog.split("\\").join("/") 83 , shLongProg 84 , pwshProg = shProg && "\"" + shProg + "$exe\"" 85 , pwshLongProg 86 shTarget = shTarget.split("\\").join("/") 87 args = args || "" 88 variables = variables || "" 89 if (!prog) { 90 prog = "\"%dp0%\\" + target + "\"" 91 shProg = "\"$basedir/" + shTarget + "\"" 92 pwshProg = shProg 93 args = "" 94 target = "" 95 shTarget = "" 96 } else { 97 longProg = "\"%dp0%\\" + prog + ".exe\"" 98 shLongProg = "\"$basedir/" + prog + "\"" 99 pwshLongProg = "\"$basedir/" + prog + "$exe\"" 100 target = "\"%dp0%\\" + target + "\"" 101 shTarget = "\"$basedir/" + shTarget + "\"" 102 } 103 104 // @SETLOCAL 105 // @CALL :find_dp0 106 // 107 // @IF EXIST "%dp0%\node.exe" ( 108 // @SET "_prog=%dp0%\node.exe" 109 // ) ELSE ( 110 // @SET "_prog=node" 111 // @SET PATHEXT=%PATHEXT:;.JS;=;% 112 // ) 113 // 114 // "%_prog%" "%dp0%\.\node_modules\npm\bin\npm-cli.js" %* 115 // @ENDLOCAL 116 // @EXIT /b %errorlevel% 117 // 118 // :find_dp0 119 // SET dp0=%~dp0 120 // EXIT /b 121 // 122 // Subroutine trick to fix https://github.com/npm/cmd-shim/issues/10 123 var head = '@ECHO off\r\n' + 124 'SETLOCAL\r\n' + 125 'CALL :find_dp0\r\n' 126 var foot = 'ENDLOCAL\r\n' + 127 'EXIT /b %errorlevel%\r\n' + 128 ':find_dp0\r\n' + 129 'SET dp0=%~dp0\r\n' + 130 'EXIT /b\r\n' 131 132 var cmd 133 if (longProg) { 134 shLongProg = shLongProg.trim(); 135 args = args.trim(); 136 var variableDeclarationsAsBatch = toBatchSyntax.convertToSetCommands(variables) 137 cmd = head 138 + variableDeclarationsAsBatch 139 + "\r\n" 140 + "IF EXIST " + longProg + " (\r\n" 141 + " SET \"_prog=" + longProg.replace(/(^")|("$)/g, '') + "\"\r\n" 142 + ") ELSE (\r\n" 143 + " SET \"_prog=" + prog.replace(/(^")|("$)/g, '') + "\"\r\n" 144 + " SET PATHEXT=%PATHEXT:;.JS;=;%\r\n" 145 + ")\r\n" 146 + "\r\n" 147 + "\"%_prog%\" " + args + " " + target + " %*\r\n" 148 + foot 149 } else { 150 cmd = head + prog + " " + args + " " + target + " %*\r\n" + foot 151 } 152 153 // #!/bin/sh 154 // basedir=`dirname "$0"` 155 // 156 // case `uname` in 157 // *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;; 158 // esac 159 // 160 // if [ -x "$basedir/node.exe" ]; then 161 // "$basedir/node.exe" "$basedir/node_modules/npm/bin/npm-cli.js" "$@" 162 // ret=$? 163 // else 164 // node "$basedir/node_modules/npm/bin/npm-cli.js" "$@" 165 // ret=$? 166 // fi 167 // exit $ret 168 169 var sh = "#!/bin/sh\n" 170 171 sh = sh 172 + "basedir=$(dirname \"$(echo \"$0\" | sed -e 's,\\\\,/,g')\")\n" 173 + "\n" 174 + "case `uname` in\n" 175 + " *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w \"$basedir\"`;;\n" 176 + "esac\n" 177 + "\n" 178 179 if (shLongProg) { 180 sh = sh 181 + "if [ -x "+shLongProg+" ]; then\n" 182 + " " + variables + shLongProg + " " + args + " " + shTarget + " \"$@\"\n" 183 + " ret=$?\n" 184 + "else \n" 185 + " " + variables + shProg + " " + args + " " + shTarget + " \"$@\"\n" 186 + " ret=$?\n" 187 + "fi\n" 188 + "exit $ret\n" 189 } else { 190 sh = sh 191 + shProg + " " + args + " " + shTarget + " \"$@\"\n" 192 + "exit $?\n" 193 } 194 195 // #!/usr/bin/env pwsh 196 // $basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent 197 // 198 // $ret=0 199 // $exe = "" 200 // if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) { 201 // # Fix case when both the Windows and Linux builds of Node 202 // # are installed in the same directory 203 // $exe = ".exe" 204 // } 205 // if (Test-Path "$basedir/node") { 206 // & "$basedir/node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args 207 // $ret=$LASTEXITCODE 208 // } else { 209 // & "node$exe" "$basedir/node_modules/npm/bin/npm-cli.js" $args 210 // $ret=$LASTEXITCODE 211 // } 212 // exit $ret 213 var pwsh = "#!/usr/bin/env pwsh\n" 214 + "$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent\n" 215 + "\n" 216 + "$exe=\"\"\n" 217 + "if ($PSVersionTable.PSVersion -lt \"6.0\" -or $IsWindows) {\n" 218 + " # Fix case when both the Windows and Linux builds of Node\n" 219 + " # are installed in the same directory\n" 220 + " $exe=\".exe\"\n" 221 + "}\n" 222 if (shLongProg) { 223 pwsh = pwsh 224 + "$ret=0\n" 225 + "if (Test-Path " + pwshLongProg + ") {\n" 226 + " & " + pwshLongProg + " " + args + " " + shTarget + " $args\n" 227 + " $ret=$LASTEXITCODE\n" 228 + "} else {\n" 229 + " & " + pwshProg + " " + args + " " + shTarget + " $args\n" 230 + " $ret=$LASTEXITCODE\n" 231 + "}\n" 232 + "exit $ret\n" 233 } else { 234 pwsh = pwsh 235 + "& " + pwshProg + " " + args + " " + shTarget + " $args\n" 236 + "exit $LASTEXITCODE\n" 237 } 238 239 var then = times(3, next, cb) 240 fs.writeFile(to + ".ps1", pwsh, "utf8", then) 241 fs.writeFile(to + ".cmd", cmd, "utf8", then) 242 fs.writeFile(to, sh, "utf8", then) 243 function next () { 244 chmodShim(to, cb) 245 } 246} 247 248function chmodShim (to, cb) { 249 var then = times(3, cb, cb) 250 fs.chmod(to, "0755", then) 251 fs.chmod(to + ".cmd", "0755", then) 252 fs.chmod(to + ".ps1", "0755", then) 253} 254 255function times(n, ok, cb) { 256 var errState = null 257 return function(er) { 258 if (!errState) { 259 if (er) 260 cb(errState = er) 261 else if (--n === 0) 262 ok() 263 } 264 } 265} 266