summary refs log tree commit diff stats
path: root/tools/downloader.nim
blob: 3d866f5d733cfde053d24ae2f3db8c9b81439216 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
## Helper that is run after Nim's installation.

## We download mirror'ed mingw packages. The originals came from:
##
## https://sourceforge.net/projects/mingw-w64/files/Toolchains%20
##   targetting%20Win32/Personal%20Builds/mingw-builds/6.3.0/threads-win32/
##   dwarf/i686-6.3.0-release-win32-dwarf-rt_v5-rev1.7z/download
## https://sourceforge.net/projects/mingw-w64/files/Toolchains%20
##   targetting%20Win64/Personal%20Builds/mingw-builds/6.3.0/threads-win32/
##   seh/x86_64-6.3.0-release-win32-seh-rt_v5-rev1.7z/download
##


import
  ui, asyncdispatch, httpclient, os, finish, registry, strutils, osproc

type
  Actions = object
    addToPath, startMenu, mingw, aporia: bool
  Controls = object
    apply: Button
    bar: ProgressBar
    lab: Label

const arch = $(sizeof(int)*8)

proc download(pkg: string; c: Controls) {.async.} =
  let z = r"..\dist" / pkg & ".7z"
  if fileExists(z):
    c.lab.text = z & " already exists"
    return
  c.bar.value = 0
  var client = newAsyncHttpClient()
  proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
    c.lab.text = "Downloading " & pkg & " " & $(speed div 1000) & "kb/s"
    c.bar.value = clamp(int(progress*100 div total), 0, 100)

  client.onProgressChanged = onProgressChanged
  await client.downloadFile("https://nim-lang.org/download/" & pkg & ".7z", z)
  c.bar.value = 100
  let p = osproc.startProcess("7zG.exe", getCurrentDir() / r"..\dist",
                              ["x", pkg & ".7z"])
  if p.waitForExit != 0:
    c.lab.text = "Unpacking failed: " & z

proc apply(a: Actions; c: Controls) {.async.} =
  if a.mingw:
    await download("mingw" & arch & "-6.3.0", c)
  if a.aporia:
    await download("aporia-0.4.0", c)

  if a.addToPath:
    let desiredPath = expandFilename(getCurrentDir() / "bin")
    let p = getUnicodeValue(r"Environment", "Path",
      HKEY_CURRENT_USER)
    var alreadyInPath = false
    for x in p.split(';'):
      if x.len == 0: continue
      let y = try: expandFilename(if x[0] == '"' and x[^1] == '"':
                                    substr(x, 1, x.len-2) else: x)
              except: ""
      if y == desiredPath: alreadyInPath = true
    if not alreadyInPath:
      addToPathEnv(desiredPath)

  if a.startMenu:
    createStartMenuEntry()

  c.apply.text = "Quit"

proc main() =
  var mainwin = newWindow("Nim installer", 640, 280, true)
  mainwin.margined = true
  mainwin.onClosing = (proc (): bool = return true)

  let box = newVerticalBox(true)
  mainwin.setChild(box)

  var groupA = newGroup("Actions", true)
  box.add(groupA, false)
  var innerA = newVerticalBox(true)
  groupA.child = innerA

  let cbAddToPath = newCheckbox("Add Nim to PATH")
  innerA.add cbAddToPath
  let cbStartMenu = newCheckbox("Create start menu entry")
  innerA.add cbStartMenu

  var groupB = newGroup("Optional Components", true)
  box.add(groupB, false)
  var innerB = newVerticalBox(true)
  groupB.child = innerB

  let cbMingw = newCheckbox("Download Mingw")
  innerB.add cbMingw

  let cbAporia = newCheckbox("Download Aporia")
  innerB.add cbAporia

  var c = Controls(
    apply: newButton("Apply"),
    bar: newProgressBar(),
    lab: newLabel(""))

  innerB.add c.apply
  innerB.add c.bar
  innerB.add c.lab

  proc apply() =
    c.apply.text = "Abort"
    asyncCheck apply(Actions(addToPath: cbAddToPath.checked,
                  startMenu: cbStartMenu.checked,
                  mingw: cbMingw.checked,
                  aporia: cbAporia.checked), c)

    c.apply.onclick = proc () =
      ui.quit()
      system.quit()

  c.apply.onclick = apply

  show(mainwin)
  pollingMainLoop((proc (timeout: int) =
    if hasPendingOperations(): asyncdispatch.poll(timeout)), 10)

init()
main()