niminst.nim 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752
  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. when defined(nimPreviewSlimSystem):
  13. import std/syncio
  14. when not defined(nimHasEffectsOf):
  15. {.pragma: effectsOf.}
  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. func 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. func firstBinPath(c: ConfigData): string =
  100. if c.binPaths.len > 0: result = c.binPaths[0]
  101. else: result = ""
  102. func `\`(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. func 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
  152. case kind
  153. of cmdArgument:
  154. if c.actions == {}:
  155. for a in split(normalize(key), {';', ','}):
  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, "ini")
  167. c.nimArgs = cmdLineRest(p)
  168. break
  169. of cmdLongOption, cmdShortOption:
  170. case normalize(key)
  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 {.effectsOf: t.} =
  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. func tPath(c: char): char =
  204. if c == '\\': '/'
  205. else: c
  206. func 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 dirExists(p):
  231. walkDirRecursively(s, p, p, false)
  232. else:
  233. var i = 0
  234. for f in walkPattern(p):
  235. if dirExists(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. func 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. func 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. func buildDir(os, cpu: int): string =
  431. "c_code" / ($os & "_" & $cpu)
  432. func 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. if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue
  449. for dup in mitems(c.cfiles[osA][cpuA]):
  450. let key = $secureHashFile(build / dup)
  451. let val = buildDir(osA, cpuA) / extractFilename(dup)
  452. let orig = tab.getOrDefault(key)
  453. if orig.len > 0:
  454. # file is identical, so delete duplicate:
  455. removeFile(dup)
  456. dup = orig
  457. else:
  458. tab[key] = val
  459. proc writeInstallScripts(c: var ConfigData) =
  460. if c.installScript:
  461. writeFile(installShFile, generateInstallScript(c), "\10")
  462. inclFilePermissions(installShFile, {fpUserExec, fpGroupExec, fpOthersExec})
  463. if c.uninstallScript:
  464. writeFile(deinstallShFile, generateDeinstallScript(c), "\10")
  465. inclFilePermissions(deinstallShFile, {fpUserExec, fpGroupExec, fpOthersExec})
  466. template gatherFiles(fun, libpath, outDir) =
  467. block:
  468. template copySrc(src) =
  469. let dst = outDir / extractFilename(src)
  470. when false: echo (dst, dst)
  471. fun(src, dst)
  472. for f in walkFiles(libpath / "lib/*.h"): copySrc(f)
  473. # commenting out for now, see discussion in https://github.com/nim-lang/Nim/pull/13413
  474. # copySrc(libpath / "lib/wrappers/linenoise/linenoise.h")
  475. proc exe(f: string): string =
  476. result = addFileExt(f, ExeExt)
  477. when defined(windows):
  478. result = result.replace('/','\\')
  479. proc findNim(): string =
  480. let nim = "nim".exe
  481. result = quoteShell("bin" / nim)
  482. if not fileExists(result):
  483. result = "nim"
  484. proc srcdist(c: var ConfigData) =
  485. let cCodeDir = getOutputDir(c) / "c_code"
  486. if not dirExists(cCodeDir): createDir(cCodeDir)
  487. gatherFiles(copyFile, c.libpath, cCodeDir)
  488. var winIndex = -1
  489. var intel32Index = -1
  490. var intel64Index = -1
  491. for osA in 1..c.oses.len:
  492. let osname = c.oses[osA-1]
  493. if osname.cmpIgnoreStyle("windows") == 0: winIndex = osA
  494. for cpuA in 1..c.cpus.len:
  495. if c.explicitPlatforms and not c.platforms[osA][cpuA]: continue
  496. let cpuname = c.cpus[cpuA-1]
  497. if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA
  498. elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA
  499. var dir = getOutputDir(c) / buildDir(osA, cpuA)
  500. if dirExists(dir): removeDir(dir)
  501. createDir(dir)
  502. var cmd = ("$# compile -f --incremental:off --compileonly " &
  503. "--gen_mapping --cc:gcc --skipUserCfg" &
  504. " --os:$# --cpu:$# $# $#") %
  505. [findNim(), osname, cpuname, c.nimArgs, c.mainfile]
  506. echo(cmd)
  507. if execShellCmd(cmd) != 0:
  508. quit("Error: call to nim compiler failed")
  509. readCFiles(c, osA, cpuA)
  510. for i in 0 .. c.cfiles[osA][cpuA].len-1:
  511. let dest = dir / extractFilename(c.cfiles[osA][cpuA][i])
  512. let relDest = buildDir(osA, cpuA) / extractFilename(c.cfiles[osA][cpuA][i])
  513. copyFile(dest=dest, source=c.cfiles[osA][cpuA][i])
  514. c.cfiles[osA][cpuA][i] = relDest
  515. # second pass: remove duplicate files
  516. deduplicateFiles(c)
  517. writeFile(getOutputDir(c) / buildShFile, generateBuildShellScript(c), "\10")
  518. inclFilePermissions(getOutputDir(c) / buildShFile, {fpUserExec, fpGroupExec, fpOthersExec})
  519. writeFile(getOutputDir(c) / makeFile, generateMakefile(c), "\10")
  520. if winIndex >= 0:
  521. if intel32Index >= 0 or intel64Index >= 0:
  522. writeFile(getOutputDir(c) / buildBatFile,
  523. generateBuildBatchScript(c, winIndex, intel32Index, intel64Index), "\13\10")
  524. if intel32Index >= 0:
  525. writeFile(getOutputDir(c) / buildBatFile32, "SET ARCH=32\nCALL build.bat\n")
  526. if intel64Index >= 0:
  527. writeFile(getOutputDir(c) / buildBatFile64, "SET ARCH=64\nCALL build.bat\n")
  528. writeInstallScripts(c)
  529. # --------------------- generate inno setup -----------------------------------
  530. proc setupDist(c: var ConfigData) =
  531. let scrpt = generateInnoSetup(c)
  532. let n = "build" / "install_$#_$#.iss" % [toLowerAscii(c.name), c.version]
  533. writeFile(n, scrpt, "\13\10")
  534. when defined(windows):
  535. if c.innosetup.path.len == 0:
  536. c.innosetup.path = "iscc.exe"
  537. let outcmd = if c.outdir.len == 0: "build" else: c.outdir
  538. let cmd = "$# $# /O$# $#" % [quoteShell(c.innosetup.path),
  539. c.innosetup.flags, outcmd, n]
  540. echo(cmd)
  541. if execShellCmd(cmd) == 0:
  542. removeFile(n)
  543. else:
  544. quit("External program failed")
  545. # --------------------- generate NSIS setup -----------------------------------
  546. proc setupDist2(c: var ConfigData) =
  547. let scrpt = generateNsisSetup(c)
  548. let n = "build" / "install_$#_$#.nsi" % [toLowerAscii(c.name), c.version]
  549. writeFile(n, scrpt, "\13\10")
  550. when defined(windows):
  551. if c.nsisSetup.path.len == 0:
  552. c.nsisSetup.path = "makensis.exe"
  553. let outcmd = if c.outdir.len == 0: "build" else: c.outdir
  554. let cmd = "$# $# /O$# $#" % [quoteShell(c.nsisSetup.path),
  555. c.nsisSetup.flags, outcmd, n]
  556. echo(cmd)
  557. if execShellCmd(cmd) == 0:
  558. removeFile(n)
  559. else:
  560. quit("External program failed")
  561. proc xzDist(c: var ConfigData; windowsZip=false) =
  562. let proj = toLowerAscii(c.name) & "-" & c.version
  563. let tmpDir = if c.outdir.len == 0: "build" else: c.outdir
  564. proc processFile(destFile, src: string) =
  565. let dest = tmpDir / destFile
  566. when false: echo "Copying ", src, " to ", dest
  567. if not fileExists(src):
  568. echo "[Warning] Source file doesn't exist: ", src
  569. let destDir = dest.splitFile.dir
  570. if not dirExists(destDir): createDir(destDir)
  571. copyFileWithPermissions(src, dest)
  572. if not windowsZip and not fileExists("build" / buildBatFile):
  573. quit("No C sources found in ./build/, please build by running " &
  574. "./koch csource -d:danger.")
  575. if not windowsZip:
  576. processFile(proj / buildBatFile, "build" / buildBatFile)
  577. processFile(proj / buildBatFile32, "build" / buildBatFile32)
  578. processFile(proj / buildBatFile64, "build" / buildBatFile64)
  579. processFile(proj / buildShFile, "build" / buildShFile)
  580. processFile(proj / makeFile, "build" / makeFile)
  581. processFile(proj / installShFile, installShFile)
  582. processFile(proj / deinstallShFile, deinstallShFile)
  583. template processFileAux(src, dst) = processFile(dst, src)
  584. gatherFiles(processFileAux, c.libpath, proj / "c_code")
  585. for osA in 1..c.oses.len:
  586. for cpuA in 1..c.cpus.len:
  587. var dir = buildDir(osA, cpuA)
  588. for k, f in walkDir("build" / dir):
  589. if k == pcFile: processFile(proj / dir / extractFilename(f), f)
  590. else:
  591. for f in items(c.cat[fcWinBin]):
  592. let filename = f.extractFilename
  593. processFile(proj / "bin" / filename, f)
  594. let osSpecific = if windowsZip: fcWindows else: fcUnix
  595. for cat in items({fcConfig..fcOther, osSpecific, fcNimble}):
  596. echo("Current category: ", cat)
  597. for f in items(c.cat[cat]): processFile(proj / f, f)
  598. # Copy the .nimble file over
  599. let nimbleFile = c.nimblePkgName & ".nimble"
  600. processFile(proj / nimbleFile, nimbleFile)
  601. when true:
  602. let oldDir = getCurrentDir()
  603. setCurrentDir(tmpDir)
  604. try:
  605. if windowsZip:
  606. if execShellCmd("7z a -tzip $1.zip $1" % proj) != 0:
  607. echo("External program failed (zip)")
  608. when false:
  609. writeFile("config.txt", """;!@Install@!UTF-8!
  610. Title="Nim v$1"
  611. BeginPrompt="Do you want to configure Nim v$1?"
  612. RunProgram="tools\downloader.exe"
  613. ;!@InstallEnd@!""" % NimVersion)
  614. if execShellCmd("7z a -sfx7zS2.sfx -t7z $1.exe $1" % proj) != 0:
  615. echo("External program failed (7z)")
  616. else:
  617. if execShellCmd("gtar cf $1.tar --exclude=.DS_Store $1" %
  618. proj) != 0:
  619. # try old 'tar' without --exclude feature:
  620. if execShellCmd("tar cf $1.tar $1" % proj) != 0:
  621. echo("External program failed")
  622. if execShellCmd("xz -9f $1.tar" % proj) != 0:
  623. echo("External program failed")
  624. finally:
  625. setCurrentDir(oldDir)
  626. # -- prepare build files for .deb creation
  627. proc debDist(c: var ConfigData) =
  628. if not fileExists(getOutputDir(c) / "build.sh"): quit("No build.sh found.")
  629. if not fileExists(getOutputDir(c) / "install.sh"): quit("No install.sh found.")
  630. if c.debOpts.shortDesc == "": quit("shortDesc must be set in the .ini file.")
  631. if c.debOpts.licenses.len == 0:
  632. echo("[Warning] No licenses specified for .deb creation.")
  633. # -- Copy files into /tmp/..
  634. echo("Copying source to tmp/niminst/deb/")
  635. var currentSource = getCurrentDir()
  636. var workingDir = getTempDir() / "niminst" / "deb"
  637. var upstreamSource = (c.name.toLowerAscii() & "-" & c.version)
  638. createDir(workingDir / upstreamSource)
  639. template copyNimDist(f, dest: string) =
  640. createDir((workingDir / upstreamSource / dest).splitFile.dir)
  641. copyFile(currentSource / f, workingDir / upstreamSource / dest)
  642. # Don't copy all files, only the ones specified in the config:
  643. copyNimDist(buildShFile, buildShFile)
  644. copyNimDist(makeFile, makeFile)
  645. copyNimDist(installShFile, installShFile)
  646. createDir(workingDir / upstreamSource / "build")
  647. gatherFiles(copyNimDist, c.libpath, "build")
  648. for osA in 1..c.oses.len:
  649. for cpuA in 1..c.cpus.len:
  650. var dir = buildDir(osA, cpuA)
  651. for k, f in walkDir(dir):
  652. if k == pcFile: copyNimDist(f, dir / extractFilename(f))
  653. for cat in items({fcConfig..fcOther, fcUnix}):
  654. for f in items(c.cat[cat]): copyNimDist(f, f)
  655. # -- Create necessary build files for debhelper.
  656. let mtnName = c.vars["mtnname"]
  657. let mtnEmail = c.vars["mtnemail"]
  658. prepDeb(c.name, c.version, mtnName, mtnEmail, c.debOpts.shortDesc,
  659. c.description, c.debOpts.licenses, c.cat[fcUnixBin], c.cat[fcConfig],
  660. c.cat[fcDoc], c.cat[fcLib], c.debOpts.buildDepends,
  661. c.debOpts.pkgDepends)
  662. # ------------------- main ----------------------------------------------------
  663. proc main() =
  664. var c: ConfigData
  665. iniConfigData(c)
  666. parseCmdLine(c)
  667. parseIniFile(c)
  668. if actionInno in c.actions:
  669. setupDist(c)
  670. if actionNsis in c.actions:
  671. setupDist2(c)
  672. if actionCSource in c.actions:
  673. srcdist(c)
  674. if actionScripts in c.actions:
  675. writeInstallScripts(c)
  676. if actionZip in c.actions:
  677. xzDist(c, true)
  678. if actionXz in c.actions:
  679. xzDist(c)
  680. if actionDeb in c.actions:
  681. debDist(c)
  682. when isMainModule:
  683. main()