niminst.nim 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. #
  2. #
  3. # The Nim Installation Generator
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import
  10. os, strutils, parseopt, parsecfg, strtabs, streams, debcreation,
  11. std / sha1
  12. const
  13. maxOS = 20 # max number of OSes
  14. maxCPU = 20 # max number of CPUs
  15. buildShFile = "build.sh"
  16. buildBatFile = "build.bat"
  17. buildBatFile32 = "build32.bat"
  18. buildBatFile64 = "build64.bat"
  19. makeFile = "makefile"
  20. installShFile = "install.sh"
  21. deinstallShFile = "deinstall.sh"
  22. type
  23. AppType = enum appConsole, appGUI
  24. Action = enum
  25. actionNone, # action not yet known
  26. actionCSource # action: create C sources
  27. actionInno, # action: create Inno Setup installer
  28. actionNsis, # action: create NSIS installer
  29. actionScripts # action: create install and deinstall scripts
  30. actionZip # action: create zip file
  31. actionXz, # action: create xz file
  32. actionDeb # action: prepare deb package
  33. FileCategory = enum
  34. fcWinBin, # binaries for Windows
  35. fcConfig, # configuration files
  36. fcData, # data files
  37. fcDoc, # documentation files
  38. fcLib, # library files
  39. fcOther, # other files; will not be copied on UNIX
  40. fcWindows, # files only for Windows
  41. fcUnix, # files only for Unix; must be after ``fcWindows``
  42. fcUnixBin, # binaries for Unix
  43. fcDocStart, # links to documentation for Windows installer
  44. fcNimble # nimble package files to copy to /opt/nimble/pkgs/pkg-ver
  45. ConfigData = object of RootObj
  46. actions: set[Action]
  47. cat: array[FileCategory, seq[string]]
  48. binPaths, authors, oses, cpus, downloads: seq[string]
  49. cfiles: array[1..maxOS, array[1..maxCPU, seq[string]]]
  50. platforms: array[1..maxOS, array[1..maxCPU, bool]]
  51. ccompiler, linker, innosetup, nsisSetup: tuple[path, flags: string]
  52. name, displayName, version, description, license, infile, outdir: string
  53. mainfile, libpath: string
  54. innoSetupFlag, installScript, uninstallScript: bool
  55. explicitPlatforms: bool
  56. vars: StringTableRef
  57. app: AppType
  58. nimArgs: string
  59. debOpts: TDebOptions
  60. nimblePkgName: string
  61. const
  62. unixDirVars: array[fcConfig..fcLib, string] = [
  63. "$configdir", "$datadir", "$docdir", "$libdir"
  64. ]
  65. func iniConfigData(c: var ConfigData) =
  66. c.actions = {}
  67. for i in low(FileCategory)..high(FileCategory): c.cat[i] = @[]
  68. c.binPaths = @[]
  69. c.authors = @[]
  70. c.oses = @[]
  71. c.cpus = @[]
  72. c.downloads = @[]
  73. c.ccompiler = ("", "")
  74. c.linker = ("", "")
  75. c.innosetup = ("", "")
  76. c.nsisSetup = ("", "")
  77. c.name = ""
  78. c.displayName = ""
  79. c.version = ""
  80. c.description = ""
  81. c.license = ""
  82. c.infile = ""
  83. c.mainfile = ""
  84. c.outdir = ""
  85. c.nimArgs = ""
  86. c.libpath = ""
  87. c.innoSetupFlag = false
  88. c.installScript = false
  89. c.uninstallScript = false
  90. c.vars = newStringTable(modeStyleInsensitive)
  91. c.debOpts.buildDepends = ""
  92. c.debOpts.pkgDepends = ""
  93. c.debOpts.shortDesc = ""
  94. c.debOpts.licenses = @[]
  95. func firstBinPath(c: ConfigData): string =
  96. if c.binPaths.len > 0: result = c.binPaths[0]
  97. else: result = ""
  98. func `\`(a, b: string): string =
  99. result = if a.len == 0: b else: a & '\\' & b
  100. template toUnix(s: string): string = s.replace('\\', '/')
  101. template toWin(s: string): string = s.replace('/', '\\')
  102. func skipRoot(f: string): string =
  103. # "abc/def/xyz" --> "def/xyz"
  104. var i = 0
  105. result = ""
  106. for component in split(f, {DirSep, AltSep}):
  107. if i > 0: result = result / component
  108. inc i
  109. if result.len == 0: result = f
  110. include "inno.nimf"
  111. include "nsis.nimf"
  112. include "buildsh.nimf"
  113. include "makefile.nimf"
  114. include "buildbat.nimf"
  115. include "install.nimf"
  116. include "deinstall.nimf"
  117. # ------------------------- configuration file -------------------------------
  118. const
  119. Version = "1.0"
  120. Usage = "niminst - Nim Installation Generator Version " & Version & """
  121. (c) 2015 Andreas Rumpf
  122. Usage:
  123. niminst [options] command[;command2...] ini-file[.ini] [compile_options]
  124. Command:
  125. csource build C source code for source based installations
  126. scripts build install and deinstall scripts
  127. zip build the ZIP file
  128. inno build the Inno Setup installer
  129. nsis build the NSIS Setup installer
  130. deb create files for debhelper
  131. Options:
  132. -o, --output:dir set the output directory
  133. -m, --main:file set the main nim file, by default ini-file with .nim
  134. extension
  135. --var:name=value set the value of a variable
  136. -h, --help shows this help
  137. -v, --version shows the version
  138. Compile_options:
  139. will be passed to the Nim compiler
  140. """
  141. proc parseCmdLine(c: var ConfigData) =
  142. var p = initOptParser()
  143. while true:
  144. next(p)
  145. var kind = p.kind
  146. var key = p.key
  147. var val = p.val.string
  148. case kind
  149. of cmdArgument:
  150. if c.actions == {}:
  151. for a in split(normalize(key.string), {';', ','}):
  152. case a
  153. of "csource": incl(c.actions, actionCSource)
  154. of "scripts": incl(c.actions, actionScripts)
  155. of "zip": incl(c.actions, actionZip)
  156. of "xz": incl(c.actions, actionXz)
  157. of "inno": incl(c.actions, actionInno)
  158. of "nsis": incl(c.actions, actionNsis)
  159. of "deb": incl(c.actions, actionDeb)
  160. else: quit(Usage)
  161. else:
  162. c.infile = addFileExt(key.string, "ini")
  163. c.nimArgs = cmdLineRest(p).string
  164. break
  165. of cmdLongOption, cmdShortOption:
  166. case normalize(key.string)
  167. of "help", "h":
  168. stdout.write(Usage)
  169. quit(0)
  170. of "version", "v":
  171. stdout.write(Version & "\n")
  172. quit(0)
  173. of "o", "output": c.outdir = val
  174. of "m", "main": c.mainfile = changeFileExt(val, "nim")
  175. of "var":
  176. var idx = val.find('=')
  177. if idx < 0: quit("invalid command line")
  178. c.vars[substr(val, 0, idx-1)] = substr(val, idx+1)
  179. else: quit(Usage)
  180. of cmdEnd: break
  181. if c.infile.len == 0: quit(Usage)
  182. if c.mainfile.len == 0: c.mainfile = changeFileExt(c.infile, "nim")
  183. proc eqT(a, b: string; t: proc (a: char): char{.nimcall.}): bool =
  184. ## equality under a transformation ``t``. candidate for the stdlib?
  185. var i = 0
  186. var j = 0
  187. while i < a.len and j < b.len:
  188. let aa = t a[i]
  189. let bb = t b[j]
  190. if aa == '\0':
  191. inc i
  192. if bb == '\0': inc j
  193. elif bb == '\0': inc j
  194. else:
  195. if aa != bb: return false
  196. inc i
  197. inc j
  198. result = i >= a.len and j >= b.len
  199. func tPath(c: char): char =
  200. if c == '\\': '/'
  201. else: c
  202. func ignoreFile(f, explicit: string, allowHtml: bool): bool =
  203. let (_, name, ext) = splitFile(f)
  204. let html = if not allowHtml: ".html" else: ""
  205. result = (ext in ["", ".exe", ".idx", ".o", ".obj", ".dylib"] or
  206. ext == html or name[0] == '.') and not eqT(f, explicit, tPath)
  207. proc walkDirRecursively(s: var seq[string], root, explicit: string,
  208. allowHtml: bool) =
  209. let tail = splitPath(root).tail
  210. if tail == "nimcache" or tail[0] == '.':
  211. return
  212. let allowHtml = allowHtml or tail == "doc"
  213. for k, f in walkDir(root):
  214. if f[0] == '.' and root[0] != '.':
  215. discard "skip .git directories etc"
  216. else:
  217. case k
  218. of pcFile, pcLinkToFile:
  219. if not ignoreFile(f, explicit, allowHtml):
  220. add(s, unixToNativePath(f))
  221. of pcDir:
  222. walkDirRecursively(s, f, explicit, allowHtml)
  223. of pcLinkToDir: discard
  224. proc addFiles(s: var seq[string], patterns: seq[string]) =
  225. for p in items(patterns):
  226. if dirExists(p):
  227. walkDirRecursively(s, p, p, false)
  228. else:
  229. var i = 0
  230. for f in walkPattern(p):
  231. if dirExists(f):
  232. walkDirRecursively(s, f, p, false)
  233. elif not ignoreFile(f, p, false):
  234. add(s, unixToNativePath(f))
  235. inc(i)
  236. if i == 0: echo("[Warning] No file found that matches: " & p)
  237. proc pathFlags(p: var CfgParser, k, v: string,
  238. t: var tuple[path, flags: string]) =
  239. case normalize(k)
  240. of "path": t.path = v
  241. of "flags": t.flags = v
  242. else: quit(errorStr(p, "unknown variable: " & k))
  243. proc filesOnly(p: var CfgParser, k, v: string, dest: var seq[string]) =
  244. case normalize(k)
  245. of "files": addFiles(dest, split(v, {';'}))
  246. else: quit(errorStr(p, "unknown variable: " & k))
  247. proc yesno(p: var CfgParser, v: string): bool =
  248. case normalize(v)
  249. of "yes", "y", "on", "true":
  250. result = true
  251. of "no", "n", "off", "false":
  252. result = false
  253. else: quit(errorStr(p, "unknown value; use: yes|no"))
  254. func incl(s: var seq[string], x: string): int =
  255. for i in 0 ..< s.len:
  256. if cmpIgnoreStyle(s[i], x) == 0: return i
  257. s.add(x)
  258. result = s.len-1
  259. func platforms(c: var ConfigData, v: string) =
  260. for line in splitLines(v):
  261. let p = line.find(": ")
  262. if p <= 1: continue
  263. let os = line.substr(0, p-1).strip
  264. let cpus = line.substr(p+1).strip
  265. c.oses.add(os)
  266. for cpu in cpus.split(';'):
  267. let cpuIdx = c.cpus.incl(cpu)
  268. c.platforms[c.oses.len][cpuIdx+1] = true
  269. proc parseIniFile(c: var ConfigData) =
  270. var
  271. p: CfgParser
  272. section = ""
  273. hasCpuOs = false
  274. var input = newFileStream(c.infile, fmRead)
  275. if input != nil:
  276. open(p, input, c.infile)
  277. while true:
  278. var k = next(p)
  279. case k.kind
  280. of cfgEof: break
  281. of cfgSectionStart:
  282. section = normalize(k.section)
  283. of cfgKeyValuePair:
  284. var v = `%`(k.value, c.vars, {useEnvironment, useEmpty})
  285. c.vars[k.key] = v
  286. case section
  287. of "project":
  288. case normalize(k.key)
  289. of "name": c.name = v
  290. of "displayname": c.displayName = v
  291. of "version": c.version = v
  292. of "os":
  293. c.oses = split(v, {';'})
  294. hasCpuOs = true
  295. if c.explicitPlatforms:
  296. quit(errorStr(p, "you cannot have both 'platforms' and 'os'"))
  297. of "cpu":
  298. c.cpus = split(v, {';'})
  299. hasCpuOs = true
  300. if c.explicitPlatforms:
  301. quit(errorStr(p, "you cannot have both 'platforms' and 'cpu'"))
  302. of "platforms":
  303. platforms(c, v)
  304. c.explicitPlatforms = true
  305. if hasCpuOs:
  306. quit(errorStr(p, "you cannot have both 'platforms' and 'os'"))
  307. of "authors": c.authors = split(v, {';'})
  308. of "description": c.description = v
  309. of "app":
  310. case normalize(v)
  311. of "console": c.app = appConsole
  312. of "gui": c.app = appGUI
  313. else: quit(errorStr(p, "expected: console or gui"))
  314. of "license": c.license = unixToNativePath(k.value)
  315. else: quit(errorStr(p, "unknown variable: " & k.key))
  316. of "var": discard
  317. of "winbin": filesOnly(p, k.key, v, c.cat[fcWinBin])
  318. of "config": filesOnly(p, k.key, v, c.cat[fcConfig])
  319. of "data": filesOnly(p, k.key, v, c.cat[fcData])
  320. of "documentation":
  321. case normalize(k.key)
  322. of "files": addFiles(c.cat[fcDoc], split(v, {';'}))
  323. of "start": addFiles(c.cat[fcDocStart], split(v, {';'}))
  324. else: quit(errorStr(p, "unknown variable: " & k.key))
  325. of "lib": filesOnly(p, k.key, v, c.cat[fcLib])
  326. of "other": filesOnly(p, k.key, v, c.cat[fcOther])
  327. of "windows":
  328. case normalize(k.key)
  329. of "files": addFiles(c.cat[fcWindows], split(v, {';'}))
  330. of "binpath": c.binPaths = split(v, {';'})
  331. of "innosetup": c.innoSetupFlag = yesno(p, v)
  332. of "download": c.downloads.add(v)
  333. else: quit(errorStr(p, "unknown variable: " & k.key))
  334. of "unix":
  335. case normalize(k.key)
  336. of "files": addFiles(c.cat[fcUnix], split(v, {';'}))
  337. of "installscript": c.installScript = yesno(p, v)
  338. of "uninstallscript": c.uninstallScript = yesno(p, v)
  339. else: quit(errorStr(p, "unknown variable: " & k.key))
  340. of "unixbin": filesOnly(p, k.key, v, c.cat[fcUnixBin])
  341. of "innosetup": pathFlags(p, k.key, v, c.innosetup)
  342. of "nsis": pathFlags(p, k.key, v, c.nsisSetup)
  343. of "ccompiler": pathFlags(p, k.key, v, c.ccompiler)
  344. of "linker": pathFlags(p, k.key, v, c.linker)
  345. of "deb":
  346. case normalize(k.key)
  347. of "builddepends":
  348. c.debOpts.buildDepends = v
  349. of "packagedepends", "pkgdepends":
  350. c.debOpts.pkgDepends = v
  351. of "shortdesc":
  352. c.debOpts.shortDesc = v
  353. of "licenses":
  354. # file,license;file,license;
  355. var i = 0
  356. var file = ""
  357. var license = ""
  358. var afterComma = false
  359. while i < v.len():
  360. case v[i]
  361. of ',':
  362. afterComma = true
  363. of ';':
  364. if file == "" or license == "":
  365. quit(errorStr(p, "Invalid `licenses` key."))
  366. c.debOpts.licenses.add((file, license))
  367. afterComma = false
  368. file = ""
  369. license = ""
  370. else:
  371. if afterComma: license.add(v[i])
  372. else: file.add(v[i])
  373. inc(i)
  374. else: quit(errorStr(p, "unknown variable: " & k.key))
  375. of "nimble":
  376. case normalize(k.key)
  377. of "pkgname":
  378. c.nimblePkgName = v
  379. of "pkgfiles":
  380. addFiles(c.cat[fcNimble], split(v, {';'}))
  381. else:
  382. quit(errorStr(p, "invalid key: " & k.key))
  383. else: quit(errorStr(p, "invalid section: " & section))
  384. of cfgOption: quit(errorStr(p, "syntax error"))
  385. of cfgError: quit(errorStr(p, k.msg))
  386. close(p)
  387. if c.name.len == 0: c.name = changeFileExt(extractFilename(c.mainfile), "")
  388. if c.displayName.len == 0: c.displayName = c.name
  389. else:
  390. quit("cannot open: " & c.infile)
  391. # ------------------------- generate source based installation ---------------
  392. proc readCFiles(c: var ConfigData, osA, cpuA: int) =
  393. var p: CfgParser
  394. var f = splitFile(c.infile).dir / "mapping.txt"
  395. c.cfiles[osA][cpuA] = @[]
  396. var input = newFileStream(f, fmRead)
  397. var section = ""
  398. if input != nil:
  399. open(p, input, f)
  400. while true:
  401. var k = next(p)
  402. case k.kind
  403. of cfgEof: break
  404. of cfgSectionStart:
  405. section = normalize(k.section)
  406. of cfgKeyValuePair:
  407. case section
  408. of "ccompiler": pathFlags(p, k.key, k.value, c.ccompiler)
  409. of "linker":
  410. pathFlags(p, k.key, k.value, c.linker)
  411. # HACK: we conditionally add ``-lm -ldl``, so remove them from the
  412. # linker flags:
  413. c.linker.flags = c.linker.flags.replaceWord("-lm").replaceWord(
  414. "-ldl").replaceWord("-lroot").replaceWord(
  415. "-lnetwork").strip
  416. else:
  417. if cmpIgnoreStyle(k.key, "libpath") == 0:
  418. c.libpath = k.value
  419. of cfgOption:
  420. if section == "cfiles" and cmpIgnoreStyle(k.key, "file") == 0:
  421. add(c.cfiles[osA][cpuA], k.value)
  422. of cfgError: quit(errorStr(p, k.msg))
  423. close(p)
  424. else:
  425. quit("Cannot open: " & f)
  426. func buildDir(os, cpu: int): string =
  427. "c_code" / ($os & "_" & $cpu)
  428. func getOutputDir(c: var ConfigData): string =
  429. if c.outdir.len > 0: c.outdir else: "build"
  430. proc writeFile(filename, content, newline: string) =
  431. var f: File
  432. if open(f, filename, fmWrite):
  433. for x in splitLines(content):
  434. write(f, x)
  435. write(f, newline)
  436. close(f)
  437. else:
  438. quit("Cannot open for writing: " & filename)
  439. proc deduplicateFiles(c: var ConfigData) =
  440. var tab = newStringTable()
  441. let build = getOutputDir(c)
  442. for osA in countup(1, c.oses.len):
  443. for cpuA in countup(1, c.cpus.len):
  444. if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue
  445. for dup in mitems(c.cfiles[osA][cpuA]):
  446. let key = $secureHashFile(build / dup)
  447. let val = buildDir(osA, cpuA) / extractFilename(dup)
  448. let orig = tab.getOrDefault(key)
  449. if orig.len > 0:
  450. # file is identical, so delete duplicate:
  451. removeFile(dup)
  452. dup = orig
  453. else:
  454. tab[key] = val
  455. proc writeInstallScripts(c: var ConfigData) =
  456. if c.installScript:
  457. writeFile(installShFile, generateInstallScript(c), "\10")
  458. inclFilePermissions(installShFile, {fpUserExec, fpGroupExec, fpOthersExec})
  459. if c.uninstallScript:
  460. writeFile(deinstallShFile, generateDeinstallScript(c), "\10")
  461. inclFilePermissions(deinstallShFile, {fpUserExec, fpGroupExec, fpOthersExec})
  462. template gatherFiles(fun, libpath, outDir) =
  463. block:
  464. template copySrc(src) =
  465. let dst = outDir / extractFilename(src)
  466. when false: echo (dst, dst)
  467. fun(src, dst)
  468. for f in walkFiles(libpath / "lib/*.h"): copySrc(f)
  469. # commenting out for now, see discussion in https://github.com/nim-lang/Nim/pull/13413
  470. # copySrc(libpath / "lib/wrappers/linenoise/linenoise.h")
  471. proc srcdist(c: var ConfigData) =
  472. let cCodeDir = getOutputDir(c) / "c_code"
  473. if not dirExists(cCodeDir): createDir(cCodeDir)
  474. gatherFiles(copyFile, c.libpath, cCodeDir)
  475. var winIndex = -1
  476. var intel32Index = -1
  477. var intel64Index = -1
  478. for osA in 1..c.oses.len:
  479. let osname = c.oses[osA-1]
  480. if osname.cmpIgnoreStyle("windows") == 0: winIndex = osA
  481. for cpuA in 1..c.cpus.len:
  482. if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue
  483. let cpuname = c.cpus[cpuA-1]
  484. if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA
  485. elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA
  486. var dir = getOutputDir(c) / buildDir(osA, cpuA)
  487. if dirExists(dir): removeDir(dir)
  488. createDir(dir)
  489. var cmd = ("nim compile -f --symbolfiles:off --compileonly " &
  490. "--gen_mapping --cc:gcc --skipUserCfg" &
  491. " --os:$# --cpu:$# $# $#") %
  492. [osname, cpuname, c.nimArgs, c.mainfile]
  493. echo(cmd)
  494. if execShellCmd(cmd) != 0:
  495. quit("Error: call to nim compiler failed")
  496. readCFiles(c, osA, cpuA)
  497. for i in 0 .. c.cfiles[osA][cpuA].len-1:
  498. let dest = dir / extractFilename(c.cfiles[osA][cpuA][i])
  499. let relDest = buildDir(osA, cpuA) / extractFilename(c.cfiles[osA][cpuA][i])
  500. copyFile(dest=dest, source=c.cfiles[osA][cpuA][i])
  501. c.cfiles[osA][cpuA][i] = relDest
  502. # second pass: remove duplicate files
  503. deduplicateFiles(c)
  504. writeFile(getOutputDir(c) / buildShFile, generateBuildShellScript(c), "\10")
  505. inclFilePermissions(getOutputDir(c) / buildShFile, {fpUserExec, fpGroupExec, fpOthersExec})
  506. writeFile(getOutputDir(c) / makeFile, generateMakefile(c), "\10")
  507. if winIndex >= 0:
  508. if intel32Index >= 0 or intel64Index >= 0:
  509. writeFile(getOutputDir(c) / buildBatFile,
  510. generateBuildBatchScript(c, winIndex, intel32Index, intel64Index), "\13\10")
  511. if intel32Index >= 0:
  512. writeFile(getOutputDir(c) / buildBatFile32, "SET ARCH=32\nCALL build.bat\n")
  513. if intel64Index >= 0:
  514. writeFile(getOutputDir(c) / buildBatFile64, "SET ARCH=64\nCALL build.bat\n")
  515. writeInstallScripts(c)
  516. # --------------------- generate inno setup -----------------------------------
  517. proc setupDist(c: var ConfigData) =
  518. let scrpt = generateInnoSetup(c)
  519. let n = "build" / "install_$#_$#.iss" % [toLowerAscii(c.name), c.version]
  520. writeFile(n, scrpt, "\13\10")
  521. when defined(windows):
  522. if c.innosetup.path.len == 0:
  523. c.innosetup.path = "iscc.exe"
  524. let outcmd = if c.outdir.len == 0: "build" else: c.outdir
  525. let cmd = "$# $# /O$# $#" % [quoteShell(c.innosetup.path),
  526. c.innosetup.flags, outcmd, n]
  527. echo(cmd)
  528. if execShellCmd(cmd) == 0:
  529. removeFile(n)
  530. else:
  531. quit("External program failed")
  532. # --------------------- generate NSIS setup -----------------------------------
  533. proc setupDist2(c: var ConfigData) =
  534. let scrpt = generateNsisSetup(c)
  535. let n = "build" / "install_$#_$#.nsi" % [toLowerAscii(c.name), c.version]
  536. writeFile(n, scrpt, "\13\10")
  537. when defined(windows):
  538. if c.nsisSetup.path.len == 0:
  539. c.nsisSetup.path = "makensis.exe"
  540. let outcmd = if c.outdir.len == 0: "build" else: c.outdir
  541. let cmd = "$# $# /O$# $#" % [quoteShell(c.nsisSetup.path),
  542. c.nsisSetup.flags, outcmd, n]
  543. echo(cmd)
  544. if execShellCmd(cmd) == 0:
  545. removeFile(n)
  546. else:
  547. quit("External program failed")
  548. proc xzDist(c: var ConfigData; windowsZip=false) =
  549. let proj = toLowerAscii(c.name) & "-" & c.version
  550. let tmpDir = if c.outdir.len == 0: "build" else: c.outdir
  551. proc processFile(destFile, src: string) =
  552. let dest = tmpDir / destFile
  553. when false: echo "Copying ", src, " to ", dest
  554. if not fileExists(src):
  555. echo "[Warning] Source file doesn't exist: ", src
  556. let destDir = dest.splitFile.dir
  557. if not dirExists(destDir): createDir(destDir)
  558. copyFileWithPermissions(src, dest)
  559. if not windowsZip and not fileExists("build" / buildBatFile):
  560. quit("No C sources found in ./build/, please build by running " &
  561. "./koch csource -d:danger.")
  562. if not windowsZip:
  563. processFile(proj / buildBatFile, "build" / buildBatFile)
  564. processFile(proj / buildBatFile32, "build" / buildBatFile32)
  565. processFile(proj / buildBatFile64, "build" / buildBatFile64)
  566. processFile(proj / buildShFile, "build" / buildShFile)
  567. processFile(proj / makeFile, "build" / makeFile)
  568. processFile(proj / installShFile, installShFile)
  569. processFile(proj / deinstallShFile, deinstallShFile)
  570. template processFileAux(src, dst) = processFile(dst, src)
  571. gatherFiles(processFileAux, c.libpath, proj / "c_code")
  572. for osA in 1..c.oses.len:
  573. for cpuA in 1..c.cpus.len:
  574. var dir = buildDir(osA, cpuA)
  575. for k, f in walkDir("build" / dir):
  576. if k == pcFile: processFile(proj / dir / extractFilename(f), f)
  577. else:
  578. for f in items(c.cat[fcWinBin]):
  579. let filename = f.extractFilename
  580. processFile(proj / "bin" / filename, f)
  581. let osSpecific = if windowsZip: fcWindows else: fcUnix
  582. for cat in items({fcConfig..fcOther, osSpecific, fcNimble}):
  583. echo("Current category: ", cat)
  584. for f in items(c.cat[cat]): processFile(proj / f, f)
  585. # Copy the .nimble file over
  586. let nimbleFile = c.nimblePkgName & ".nimble"
  587. processFile(proj / nimbleFile, nimbleFile)
  588. when true:
  589. let oldDir = getCurrentDir()
  590. setCurrentDir(tmpDir)
  591. try:
  592. if windowsZip:
  593. if execShellCmd("7z a -tzip $1.zip $1" % proj) != 0:
  594. echo("External program failed (zip)")
  595. when false:
  596. writeFile("config.txt", """;!@Install@!UTF-8!
  597. Title="Nim v$1"
  598. BeginPrompt="Do you want to configure Nim v$1?"
  599. RunProgram="tools\downloader.exe"
  600. ;!@InstallEnd@!""" % NimVersion)
  601. if execShellCmd("7z a -sfx7zS2.sfx -t7z $1.exe $1" % proj) != 0:
  602. echo("External program failed (7z)")
  603. else:
  604. if execShellCmd("gtar cf $1.tar --exclude=.DS_Store $1" %
  605. proj) != 0:
  606. # try old 'tar' without --exclude feature:
  607. if execShellCmd("tar cf $1.tar $1" % proj) != 0:
  608. echo("External program failed")
  609. if execShellCmd("xz -9f $1.tar" % proj) != 0:
  610. echo("External program failed")
  611. finally:
  612. setCurrentDir(oldDir)
  613. # -- prepare build files for .deb creation
  614. proc debDist(c: var ConfigData) =
  615. if not fileExists(getOutputDir(c) / "build.sh"): quit("No build.sh found.")
  616. if not fileExists(getOutputDir(c) / "install.sh"): quit("No install.sh found.")
  617. if c.debOpts.shortDesc == "": quit("shortDesc must be set in the .ini file.")
  618. if c.debOpts.licenses.len == 0:
  619. echo("[Warning] No licenses specified for .deb creation.")
  620. # -- Copy files into /tmp/..
  621. echo("Copying source to tmp/niminst/deb/")
  622. var currentSource = getCurrentDir()
  623. var workingDir = getTempDir() / "niminst" / "deb"
  624. var upstreamSource = (c.name.toLowerAscii() & "-" & c.version)
  625. createDir(workingDir / upstreamSource)
  626. template copyNimDist(f, dest: string) =
  627. createDir((workingDir / upstreamSource / dest).splitFile.dir)
  628. copyFile(currentSource / f, workingDir / upstreamSource / dest)
  629. # Don't copy all files, only the ones specified in the config:
  630. copyNimDist(buildShFile, buildShFile)
  631. copyNimDist(makeFile, makeFile)
  632. copyNimDist(installShFile, installShFile)
  633. createDir(workingDir / upstreamSource / "build")
  634. gatherFiles(copyNimDist, c.libpath, "build")
  635. for osA in 1..c.oses.len:
  636. for cpuA in 1..c.cpus.len:
  637. var dir = buildDir(osA, cpuA)
  638. for k, f in walkDir(dir):
  639. if k == pcFile: copyNimDist(f, dir / extractFilename(f))
  640. for cat in items({fcConfig..fcOther, fcUnix}):
  641. for f in items(c.cat[cat]): copyNimDist(f, f)
  642. # -- Create necessary build files for debhelper.
  643. let mtnName = c.vars["mtnname"]
  644. let mtnEmail = c.vars["mtnemail"]
  645. prepDeb(c.name, c.version, mtnName, mtnEmail, c.debOpts.shortDesc,
  646. c.description, c.debOpts.licenses, c.cat[fcUnixBin], c.cat[fcConfig],
  647. c.cat[fcDoc], c.cat[fcLib], c.debOpts.buildDepends,
  648. c.debOpts.pkgDepends)
  649. # ------------------- main ----------------------------------------------------
  650. proc main() =
  651. var c: ConfigData
  652. iniConfigData(c)
  653. parseCmdLine(c)
  654. parseIniFile(c)
  655. if actionInno in c.actions:
  656. setupDist(c)
  657. if actionNsis in c.actions:
  658. setupDist2(c)
  659. if actionCSource in c.actions:
  660. srcdist(c)
  661. if actionScripts in c.actions:
  662. writeInstallScripts(c)
  663. if actionZip in c.actions:
  664. xzDist(c, true)
  665. if actionXz in c.actions:
  666. xzDist(c)
  667. if actionDeb in c.actions:
  668. debDist(c)
  669. when isMainModule:
  670. main()