niminst.nim 27 KB

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