1#!/bin/sh 2SH=--[[ # -*- mode: lua; -*- 3## Slingshot rockspec generator. 4## 5## This file is distributed with Slingshot, and licensed under the 6## terms of the MIT license reproduced below. 7 8## ==================================================================== 9## Copyright (C) 2013-2015 Gary V. Vaughan 10## 11## Permission is hereby granted, free of charge, to any person 12## obtaining a copy of this software and associated documentation 13## files (the "Software"), to deal in the Software without restriction, 14## including without limitation the rights to use, copy, modify, merge, 15## publish, distribute, sublicense, and/or sell copies of the Software, 16## and to permit persons to whom the Software is furnished to do so, 17## subject to the following conditions: 18## 19## The above copyright notice and this permission notice shall be 20## included in all copies or substantial portions of the Software. 21## 22## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGE- 25## MENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 26## FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27## CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28## WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29## ==================================================================== 30 31 32_lua_version_re='"Lua 5."[123]*' 33_lua_binaries='lua lua5.3 lua53 lua5.2 lua52 luajit lua5.1 lua51' 34 35export LUA 36export LUA_INIT 37export LUA_INIT_5_2 38export LUA_INIT_5_3 39export LUA_PATH 40export LUA_CPATH 41 42# Be Bourne compatible 43if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then 44 emulate sh 45 NULLCMD=: 46 # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which 47 # is contrary to our usage. Disable this feature. 48 alias -g '${1+"$@"}'='"$@"' 49 setopt NO_GLOB_SUBST 50else 51 case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac 52fi 53 54# If LUA is not set, search PATH for something suitable. 55test -n "$LUA" || { 56 # Check that the supplied binary is executable and returns a compatible 57 # Lua version number. 58 func_vercheck () 59 { 60 test -x "$1" && { 61 eval 'case `'$1' -e "print (_VERSION)" 2>/dev/null` in 62 '"$_lua_version_re"') LUA='$1' ;; 63 esac' 64 } 65 } 66 67 progname=`echo "$0" |${SED-sed} 's|.*/||'` 68 69 save_IFS="$IFS" 70 LUA= 71 for x in $_lua_binaries; do 72 IFS=: 73 for dir in $PATH; do 74 IFS="$save_IFS" 75 func_vercheck "$dir/$x" 76 test -n "$LUA" && break 77 done 78 IFS="$save_IFS" 79 test -n "$LUA" && break 80 e="${e+$e\n}$progname: command not found on PATH: $x" 81 done 82} 83 84test -n "$LUA" || { 85 printf "${e+$e\n}$progname: retry after 'export LUA=/path/to/lua'\n" >&2 86 exit 1 87} 88 89LUA_INIT= 90LUA_INIT_5_2= 91LUA_INIT_5_3= 92 93# Reexecute using the interpreter suppiled in LUA, or found above. 94exec "$LUA" "$0" "$@" 95]]SH 96 97 98--[[ ============== ]]-- 99--[[ Parse options. ]]-- 100--[[ ============== ]]-- 101 102local usage = 'Usage: mkrockspecs [OPTIONS] PACKAGE VERSION [REVISION] [FILE]\n' 103 104prog = { 105 name = arg[0] and arg[0]:gsub (".*/", "") or "mkrockspecs", 106 107 opts = {}, 108} 109 110-- Print an argument processing error message, and return non-zero exit 111-- status. 112local function opterr (msg) 113 io.stderr:write (usage) 114 io.stderr:write (prog.name .. ": error: " .. msg .. ".\n") 115 io.stderr:write (prog.name .. ": Try '" .. prog.name .. " --help' for help,\n") 116 os.exit (2) 117end 118 119local function setopt (optname, arglist, i) 120 local opt = arglist[i] 121 if i + 1 > #arglist then 122 opterr ("option '" .. opt .. "' requires an argument") 123 end 124 prog.opts[optname] = arglist[i + 1] 125 return i + 1 126end 127 128local function die (msg) 129 msg:gsub ("([^\n]+)\n?", 130 function () 131 io.stderr:write (prog.name .. ": error: " .. msg.. "\n") 132 end) 133 os.exit (1) 134end 135 136prog["--help"] = function () 137 print (usage .. [[ 138 139Convert a YAML configuration file into a full rockspec. 140 141If FILE is provided, load it as the base configuration, otherwise if 142there is a 'rockspec.conf' in the current directory use that, or else 143wait for input on stdin. If FILE is '-', force reading base config- 144uration from stdin. 145 146PACKAGE and VERSION are the package name and version number as defined 147by 'configure.ac' or similar. REVISION is only required for a revised 148rockspec if the default "-1" revision was released with errors. 149 150 -b, --branch=BRANCH make git rockspec use BRANCH 151 -m, --module-dir=ROOT directory of lua-files for builtin build type 152 -r, --repository=REPO set the repository name (default=PACKAGE) 153 --help print this help, then exit 154 --version print version number, then exit 155 156Report bugs to http://github.com/gvvaughan/slingshot/issues.]]) 157 os.exit (0) 158end 159 160prog["--version"] = function () 161 print [[mkrockspecs (slingshot) 8.0.0 162Written by Gary V. Vaughan <gary@gnu.org>, 2013 163 164Copyright (C) 2013, Gary V. Vaughan 165Slingshot comes with ABSOLUTELY NO WARRANTY. 166See source files for individual license conditions.]] 167 os.exit (0) 168end 169 170prog["-b"] = function (argl, i) return setopt ("branch", argl, i) end 171prog["--branch"] = prog["-b"] 172 173prog["-m"] = function (argl, i) return setopt ("module_root", argl, i) end 174prog["--module-dir"] = prog["-m"] 175 176prog["-r"] = function (argl, i) return setopt ("repository", argl, i) end 177prog["--repository"] = prog["-r"] 178 179local nonopts 180local i = 0 181while i < #arg do 182 i = i + 1 183 local opt = arg[i] 184 185 -- Collect remaining arguments not nonopts to save back into _G.arg later. 186 if type (nonopts) == "table" then 187 table.insert (nonopts, opt) 188 189 -- Run prog.option handler. 190 elseif opt:sub (1,1) == "-" and type (prog[opt]) == "function" then 191 i = prog[opt] (arg, i) 192 193 -- End of option arguments. 194 elseif opt == "--" then 195 nonopts = {} 196 197 -- Diagnose unknown command line options. 198 elseif opt:sub (1, 1) == "-" then 199 opterr ("unrecognized option '" .. opt .. "'") 200 201 -- First non-option argument marks the end of options. 202 else 203 nonopts = { opt } 204 end 205end 206 207-- put non-option args back into global arg table. 208nonopts = nonopts or {} 209nonopts[0] = arg[0] 210_G.arg = nonopts 211 212if select ("#", ...) < 2 then 213 opterr ("only " .. select ("#", ...) .. " arguments provided") 214end 215 216local package = arg[1] 217local version = arg[2] 218local revision = arg[3] or "1" 219local conf = arg[4] or "rockspec.conf" 220 221-- Unless set explicity, assume the repo is named after the package. 222if prog.opts.repository == nil then 223 prog.opts.repository = package 224end 225 226 227--[[ ================= ]]-- 228--[[ Helper functions. ]]-- 229--[[ ================= ]]-- 230 231local ok, posix = pcall (require, "posix") 232 233files = {} 234 235if ok then 236 -- faster version if luaposix is available 237 function tree (root) 238 for f in posix.files (root) do 239 local path = root .. "/" .. f 240 if f:match ("%.lua$") then 241 table.insert (files, path) 242 elseif f == "." or f == ".." then 243 -- don't go into a loop 244 elseif posix.stat (path, "type") == "directory" then 245 tree (path) 246 end 247 end 248 end 249else 250 -- fallback version that executes ls in subshells 251 function tree (root) 252 local p = io.popen ("ls -1 " .. root .. " 2>/dev/null") 253 254 if p ~= nil then 255 local f = p:read "*l" 256 while f ~= nil do 257 if f:match ("%.lua$") then 258 table.insert (files, root .. "/" .. f) 259 else 260 tree (root .. "/" .. f) 261 end 262 f = p:read "*l" 263 end 264 end 265 end 266end 267 268local function escape_pattern (s) 269 return (string.gsub (s, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")) 270end 271 272local function loadmap (root) 273 local map = {} 274 tree (root) 275 for _, f in ipairs (files) do 276 local m = f:match ("^" .. escape_pattern (root) .. "/(.*)%.lua") 277 map [m:gsub ("/", "."):gsub ("%.init$", "")] = f:gsub ("^%./", "") 278 end 279 return map 280end 281 282 283--[[ =================== ]]-- 284--[[ Load configuration. ]]-- 285--[[ =================== ]]-- 286 287local yaml = require "lyaml" 288 289-- Slurp io.input (). 290local function slurp () 291 h = io.input () 292 if h then 293 local s = h:read "*a" 294 h:close () 295 return s 296 end 297end 298 299if conf == "-" then 300 io.input (io.stdin) 301else 302 local h = io.open (conf) 303 if h then 304 io.input (conf) 305 h:close () 306 else 307 io.input (io.stdin) 308 end 309end 310 311local spec = yaml.load (slurp ()) 312local default = { source = {} } 313 314-- url needn't be given if it is identical to homepage. 315local url 316if spec.source ~= nil then 317 url = spec.source.url 318elseif spec.description ~= nil then 319 url = spec.description.homepage 320else 321 die (conf .. ": could not find source.url or description.homepage") 322end 323url = url:gsub ("^[a-z]*://", ""):gsub ("%.git$", "") 324 325-- Interpolate default values. 326default.package = package 327default.version = version .. "-" .. revision 328 329configure_flags = "" 330if type (spec.external_dependencies) == "table" then 331 CPPFLAGS, LDFLAGS = "", "" 332 for name, vars in pairs (spec.external_dependencies) do 333 if vars.library then 334 CPPFLAGS = CPPFLAGS .. " -I$(" .. name .. "_INCDIR)" 335 LDFLAGS = LDFLAGS .. " -L$(" .. name .. "_LIBDIR)" 336 end 337 end 338 339 if string.len (CPPFLAGS) > 0 then 340 configure_flags = configure_flags .. 341 "CPPFLAGS='" .. CPPFLAGS:gsub ("^%s", "") .. "'" .. 342 " LDFLAGS='" .. LDFLAGS:gsub ("^%s", "") .. "'" .. 343 " " 344 end 345end 346 347-- If we have a module root, use the luarocks "builtin" type. 348if version ~= "scm" and version ~= "git" then 349 if prog.opts.module_root ~= nil then 350 default.build = { 351 type = "builtin", 352 modules = loadmap (prog.opts.module_root), 353 } 354 elseif spec.build ~= nil and spec.build.modules ~= nil then 355 default.build = { type = "builtin" } 356 end 357end 358 359default.build = default.build or { 360 type = "command", 361 build_command = "./configure " .. 362 "LUA='$(LUA)' LUA_INCLUDE='-I$(LUA_INCDIR)' " .. configure_flags .. 363 "--prefix='$(PREFIX)' --libdir='$(LIBDIR)' --datadir='$(LUADIR)' " .. 364 "--datarootdir='$(PREFIX)' && make clean all", 365 install_command = "make install luadir='$(LUADIR)' luaexecdir='$(LIBDIR)'", 366 copy_directories = {}, 367} 368 369-- Additional spec-type dependent values. 370spec.source = spec.source or {} 371spec.build = spec.build or {} 372if version ~= "scm" and version ~= "git" then 373 spec.source.url = "http://" .. url .. "/archive/release-v" .. version .. ".zip" 374 spec.source.dir = prog.opts.repository .. "-release-v" .. version 375else 376 spec.source.url = "git://" .. url .. ".git" 377 spec.source.branch = prog.opts.branch 378 spec.build.modules = nil 379 default.build.build_command = "LUA='$(LUA)' ./bootstrap && " .. default.build.build_command 380end 381 382 383-- Recursive merge, settings from spec take precedence. Elements of src 384-- overwrite equivalent keys in dest. 385local function merge (dest, src) 386 for k, v in pairs (src) do 387 if type (v) == "table" then 388 dest[k] = merge (dest[k] or {}, src[k]) 389 else 390 dest[k] = src[k] 391 end 392 end 393 return dest 394end 395 396spec = merge (default, spec) 397 398 399--[[ ======= ]]-- 400--[[ Output. ]]-- 401--[[ ======= ]]-- 402 403-- Recursively format X, with pretty printing. 404local function format (x, indent) 405 indent = indent or "" 406 if type (x) == "table" then 407 if next (x) == nil then 408 return "{}" 409 else 410 local s = "{\n" 411 412 -- Collect and sort non-numeric keys first. 413 keys = {} 414 for k in pairs (x) do 415 if type (k) ~= "number" then table.insert (keys, k) end 416 end 417 table.sort (keys, function (a, b) return tostring (a) < tostring (b) end) 418 419 -- Display non-numeric key pairs in sort order. 420 for _, k in ipairs (keys) do 421 s = s .. indent 422 if k:match ("[^_%w]") then 423 -- wrap keys with non-%w chars in square brackets 424 s = s .. '["' .. k .. '"]' 425 else 426 s = s .. k 427 end 428 s = s .. " = " .. format (x[k], indent .. " ") .. ",\n" 429 end 430 431 -- And numeric key pairs last. 432 for i, v in ipairs (x) do 433 s = s .. indent .. format (v, indent .. " ") .. ",\n" 434 end 435 return s..indent:sub (1, -3).."}" 436 end 437 elseif type (x) == "string" then 438 return string.format ("%q", x) 439 else 440 return tostring (x) 441 end 442end 443 444-- Use the standard order for known keys. 445for _, k in ipairs { 446 "package", 447 "version", 448 "description", 449 "source", 450 "dependencies", 451 "external_dependencies", 452 "build", 453} do 454 print (k .. " = " .. format (spec[k], " ")) 455 spec[k] = nil 456end 457 458-- Output anything left in the table at the end. 459for i, v in pairs (spec) do 460 print (i .. " = " .. format (v, " ")) 461end 462 463os.exit (0) 464