aboutsummaryrefslogtreecommitdiff
path: root/src/nimpb_build.nim
blob: 660f37440e464a083b541907730a741096cdafd4 (plain)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import os
import osproc
import streams
import strformat
import strtabs
import strutils

from nimpb_buildpkg/plugin import pluginMain

when defined(windows):
    const compilerId = "win32"
elif defined(linux):
    when defined(i386):
        const arch = "x86_32"
    elif defined(amd64):
        const arch = "x86_64"
    elif defined(arm64):
        const arch = "aarch_64"
    else:
        {.fatal:"unsupported architecture".}
    const compilerId = "linux-" & arch
elif defined(macosx):
    when defined(amd64):
        const arch = "x86_64"
    else:
        {.fatal:"unsupported architecture".}
    const compilerId = "osx-" & arch
else:
    {.fatal:"unsupported platform".}

when defined(windows):
    const exeSuffix = ".exe"
else:
    const exeSuffix = ""

proc findCompiler(): string =
    let
        compilerName = &"protoc-{compilerId}{exeSuffix}"
        paths = @[
            getAppDir() / "src" / "nimpb_buildpkg" / "protobuf",
            getAppDir() / "nimpb_buildpkg" / "protobuf",
        ]

    for path in paths:
        if fileExists(path / compilerName):
            return path / compilerName

    raise newException(Exception, &"{compilerName} not found!")

proc builtinIncludeDir(compilerPath: string): string =
    parentDir(compilerPath) / "include"

template verboseEcho(x: untyped): untyped =
    if verbose:
        echo(x)

proc compileProtos*(protos: openArray[string], outdir: string,
                    includes: openArray[string], verbose: bool) =
    let command = findCompiler()
    var baseArgs: seq[string] = @[]

    add(baseArgs, &"--plugin=protoc-gen-nim={getAppFilename()}")

    for incdir in includes:
        verboseEcho(&"Adding include directory: {incdir}")
        add(baseArgs, &"-I{incdir}")

    add(baseArgs, &"-I{builtinIncludeDir(command)}")
    verboseEcho(&"Adding include directory: {builtinIncludeDir(command)}")

    add(baseArgs, &"--nim_out={outdir}")
    verboseEcho(&"Output directory: {outdir}")

    for proto in protos:
        var args = baseArgs
        add(args, proto)
        var options = {poStdErrToStdOut}
        if verbose:
            incl(options, poEchoCmd)

        let env = newStringTable("NIMPB_BUILD_PLUGIN", "1", modeCaseSensitive)

        let process = startProcess(command, workingDir="", args=args, env=env,
            options=options)
        var outp = outputStream(process)
        var outputData: string = ""
        var line = newStringOfCap(120)
        while true:
            if outp.readLine(line):
                add(outputData, line)
                add(outputData, "\n")
            elif not running(process):
                break
        var rc = peekExitCode(process)
        close(process)

        if rc != 0:
            echo(outputData)
            quit(QuitFailure)
        else:
            verboseEcho(outputData)


proc usage() {.noreturn.} =
    echo(&"""
{getAppFilename()} --out=OUTDIR [-IPATH [-IPATH]...] PROTOFILE...

    --out       The output directory for the generated files
    -I          Add a path to the set of include paths
""")
    quit(QuitFailure)

when isMainModule:
    if getEnv("NIMPB_BUILD_PLUGIN", "") == "1":
        pluginMain()
        quit(QuitSuccess)

    var includes: seq[string] = @[]
    var protos: seq[string] = @[]
    var outdir: string
    var verbose = false

    if paramCount() == 0:
        usage()

    for idx in 1..paramCount():
        let param = paramStr(idx)

        if param.startsWith("-I"):
            add(includes, param[2..^1])
        elif param.startsWith("--out="):
            outdir = param[6..^1]
        elif param == "--verbose":
            verbose = true
        elif param == "--help":
            usage()
        else:
            add(protos, param)

    if outdir == nil:
        echo("error: --out is required")
        quit(QuitFailure)

    if len(protos) == 0:
        echo("error: no input files")
        quit(QuitFailure)

    compileProtos(protos, outdir, includes, verbose)