1-- 2-- premake.lua 3-- High-level helper functions for the project exporters. 4-- Copyright (c) 2002-2015 Jason Perkins and the Premake project 5-- 6 7 local p = premake 8 9 10 11-- Store captured output text for later testing 12 13 local _captured 14 15-- The string escaping function. 16 17 local _esc = function(v) return v end 18 19-- The output settings and defaults 20 21 local _eol = "\n" 22 local _indentString = "\t" 23 local _indentLevel = 0 24 25-- Set up the global configuration scope. There can be only one. 26 27 global("root") 28 29 30 31--- 32-- Capture and store everything sent through the output stream functions 33-- premake.w(), premake.x(), and premake.out(). Retrieve the captured 34-- text using the premake.captured() function. 35-- 36-- @param fn 37-- A function to execute. Any output calls made during the execution 38-- of the function will be captured. 39-- @return 40-- The captured output. 41--- 42 43 function premake.capture(fn) 44 -- start a new capture without forgetting the old one 45 local old = _captured 46 _captured = buffered.new() 47 48 -- capture 49 fn() 50 51 -- build the result 52 local captured = p.captured() 53 54 -- free the capture buffer. 55 buffered.close(_captured) 56 57 -- restore the old capture and done 58 _captured = old 59 return captured 60 end 61 62 63 64-- 65-- Returns the captured text and stops capturing. 66-- 67 68 function premake.captured() 69 if _captured then 70 return buffered.tostring(_captured) 71 else 72 return "" 73 end 74 end 75 76 77 78--- 79-- Set the output stream end-of-line sequence. 80-- 81-- @param s 82-- The string to use to mark line ends, or nil to keep the existing 83-- EOL sequence. 84-- @return 85-- The new EOL sequence. 86--- 87 88 function premake.eol(s) 89 _eol = s or _eol 90 return _eol 91 end 92 93 94 95--- 96-- Handle escaping of strings for various outputs. 97-- 98-- @param value 99-- If this is a string: escape it and return the new value. If it is an 100-- array, return a new array of escaped values. 101-- @return 102-- If the input was a single string, returns the escaped version. If it 103-- was an array, returns an corresponding array of escaped strings. 104--- 105 106 function premake.esc(value) 107 if type(value) == "table" then 108 local result = {} 109 local n = #value 110 for i = 1, n do 111 table.insert(result, p.esc(value[i])) 112 end 113 return result 114 end 115 116 return _esc(value or "") 117 end 118 119 120 121--- 122-- Set a new string escaping function. 123-- 124-- @param func 125-- The new escaping function, which should take a single string argument 126-- and return the escaped version of that string. If nil, uses a default 127-- no-op function. 128--- 129 130 function premake.escaper(func) 131 _esc = func 132 if not _esc then 133 _esc = function (value) return value end 134 end 135 end 136 137 138 139-- 140-- Returns a boolean if the file was modified 141-- Open a file for output, and call a function to actually do the writing. 142-- Used by the actions to generate workspace and project files. 143-- 144-- @param obj 145-- A workspace or project object; will be passed to the callback function. 146-- @param ext 147-- An optional extension for the generated file, with the leading dot. 148-- @param callback 149-- The function responsible for writing the file, should take a workspace 150-- or project as a parameters. 151-- 152 153 function premake.generate(obj, ext, callback) 154 local output = p.capture(function () 155 _indentLevel = 0 156 callback(obj) 157 _indentLevel = 0 158 end) 159 160 local fn = p.filename(obj, ext) 161 162 -- make sure output folder exists. 163 local dir = path.getdirectory(fn) 164 local ok, err = os.mkdir(dir) 165 if not ok then 166 error(err, 0) 167 end 168 169 local f, err = os.writefile_ifnotequal(output, fn); 170 171 if (f == 0) then 172 return false -- file not modified 173 elseif (f < 0) then 174 error(err, 0) 175 elseif (f > 0) then 176 printf("Generated %s...", path.getrelative(os.getcwd(), fn)) 177 return true -- file modified 178 end 179 end 180 181 182 183-- 184-- Marks a file as modified without changing its contents 185-- 186-- @param obj 187-- A workspace or project object; will be passed to the callback function. 188-- @param ext 189-- An optional extension for the generated file, with the leading dot. 190-- 191 192 function premake.touch(obj, ext) 193 local fn = premake.filename(obj, ext) 194 195 -- make sure output folder exists. 196 local dir = path.getdirectory(fn) 197 local ok, err = os.mkdir(dir) 198 if not ok then 199 error(err, 0) 200 end 201 202 local f, err = os.touchfile(fn); 203 204 if (f == 0) then 205 return false -- file marked as modified 206 elseif (f < 0) then 207 error(err, 0) 208 elseif (f > 0) then 209 return true -- file created 210 end 211 end 212 213 214--- 215-- Returns the full path a file generated from any of the project 216-- objects (project, workspace, rule). 217-- 218-- @param obj 219-- The project object being generated. 220-- @param ext 221-- An optional extension for the generated file, with the leading dot. 222--- 223 224function premake.filename(obj, ext) 225 local fname = obj.location or obj.basedir 226 if ext and not ext:startswith(".") then 227 fname = path.join(fname, ext) 228 else 229 fname = path.join(fname, obj.filename) 230 if ext then 231 fname = fname .. ext 232 end 233 end 234 return path.getabsolute(fname) 235end 236 237 238 239--- 240-- Sets the output indentation parameters. 241-- 242-- @param s 243-- The indentation string. 244-- @param i 245-- The new indentation level, or nil to reset to zero. 246--- 247 248 function premake.indent(s, i) 249 _indentString = s or "\t" 250 _indentLevel = i or 0 251 end 252 253 254 255--- 256-- Write a simple, unformatted string to the output stream, with no indentation 257-- or end of line sequence. 258--- 259 260 function premake.out(s) 261 if not _captured then 262 io.write(s) 263 else 264 buffered.write(_captured, s) 265 end 266 end 267 268 269 270--- 271-- Write a simple, unformatted string to the output stream, with no indentation, 272-- and append the current EOL sequence. 273--- 274 275 function premake.outln(s) 276 p.out(s) 277 p.out(_eol or "\n") 278 end 279 280 281 282--- 283-- Write a formatted string to the exported file, after decreasing the 284-- indentation level by one. 285-- 286-- @param i 287-- If set to a number, the indentation level will be decreased by 288-- this amount. If nil, the indentation level is decremented and 289-- no output is written. Otherwise, pass to premake.w() as the 290-- formatting string, followed by any additional arguments. 291--- 292 293 function premake.pop(i, ...) 294 if i == nil or type(i) == "number" then 295 _indentLevel = _indentLevel - (i or 1) 296 else 297 _indentLevel = _indentLevel - 1 298 p.w(i, ...) 299 end 300 end 301 302 303 304--- 305-- Write a formatted string to the exported file, and increase the 306-- indentation level by one. 307-- 308-- @param i 309-- If set to a number, the indentation level will be increased by 310-- this amount. If nil, the indentation level is incremented and 311-- no output is written. Otherwise, pass to premake.w() as the 312-- formatting string, followed by any additional arguments. 313--- 314 315 function premake.push(i, ...) 316 if i == nil or type(i) == "number" then 317 _indentLevel = _indentLevel + (i or 1) 318 else 319 p.w(i, ...) 320 _indentLevel = _indentLevel + 1 321 end 322 end 323 324 325 326--- 327-- Wrap the provided value in double quotes if it contains spaces, or 328-- if it contains a shell variable of the form $(...). 329--- 330 331 function premake.quoted(value) 332 local q = value:find(" ", 1, true) 333 if not q then 334 q = value:find("$%(.-%)", 1) 335 end 336 if q then 337 value = '"' .. value .. '"' 338 end 339 return value 340 end 341 342 343 344-- 345-- Output a UTF-8 BOM to the exported file. 346-- 347 348 function p.utf8() 349 p.out('\239\187\191') 350 end 351 352 353 354--- 355-- Write a formatted string to the exported file, at the current 356-- level of indentation, and appends an end of line sequence. 357-- This gets called quite a lot, hence the very short name. 358--- 359 360 function premake.w(...) 361 if select("#", ...) > 0 then 362 p.outln(string.rep(_indentString or "\t", _indentLevel) .. string.format(...)) 363 else 364 p.outln(''); 365 end 366 end 367 368 369 370--- 371-- Write a formatted string to the exported file, after passing all 372-- arguments (except for the first, which is the formatting string) 373-- through premake.esc(). 374--- 375 376 function premake.x(msg, ...) 377 local arg = {...} 378 for i = 1, #arg do 379 arg[i] = p.esc(arg[i]) 380 end 381 p.w(msg, table.unpack(arg)) 382 end 383 384 385 386--- 387-- Write a opening XML element for a UTF-8 encoded file. Used by 388-- several different files for different actions, so makes sense 389-- to have a common call for it. 390-- 391-- @param upper 392-- If true, the encoding is written in uppercase. 393--- 394 395 function premake.xmlUtf8(upper) 396 local encoding = iif(upper, "UTF-8", "utf-8") 397 p.w('<?xml version="1.0" encoding="%s"?>', encoding) 398 end 399 400 401 402-- 403-- These are the output shortcuts that I used before switching to the 404-- indentation-aware calls above. They are still in use all over the 405-- place, including lots of community code, so let's keep them around. 406-- 407-- @param i 408-- This will either be a printf-style formatting string suitable 409-- for passing to string.format(), OR an integer number indicating 410-- the desired level of indentation. If the latter, the formatting 411-- string should be the next argument in the list. 412-- @param ... 413-- The values necessary to fill out the formatting string tokens. 414-- 415 416 function _p(i, ...) 417 if type(i) == "number" then 418 _indentLevel = i 419 p.w(...) 420 else 421 _indentLevel = 0 422 p.w(i, ...) 423 end 424 end 425 426 function _x(i, ...) 427 local arg = {...} 428 for i = 2, #arg do 429 arg[i] = p.esc(arg[i]) 430 end 431 _p(i, table.unpack(arg)) 432 end 433