Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 94 additions & 4 deletions src/common.um
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type ErrCode* = enum {
preBuild
postBuild
boxJsonError
buildError
githubApiError
}

var errStrings: []str = []str{
Expand All @@ -38,7 +40,9 @@ var errStrings: []str = []str{
"json key not found",
"pre build failed",
"post build failed",
"box.json error"
"box.json error",
"build error",
"github API error"
}

fn error*(ec: ErrCode, msg: str = ""): std::Err {
Expand Down Expand Up @@ -201,14 +205,34 @@ fn (b: ^FileBox) encode*(): str {

type PlatString* = map[os::Platform]str

fn (ps: ^PlatString) get*(): str {
if validkey(ps^, os::getPlatform()) {
return ps[os::getPlatform()]
fn (ps: ^PlatString) getByPlatform*(plat: os::Platform): str {
if validkey(ps^, plat) {
return ps[plat]
}

return ps[.unknown]
}

fn (ps: ^PlatString) get*(): str {
return ps.getByPlatform(os::getPlatform())
}

type (
Target* = struct {
sources: []str
cflags: PlatString
ldflags: PlatString
}

Build* = struct {
isValid: bool
pre: PlatString
post: PlatString
include: []str
targets: map[str]Target
}
)

type Meta* = struct {
name: str
version: str
Expand All @@ -224,6 +248,7 @@ type Meta* = struct {
preBuild: PlatString
postBuild: PlatString
others: map[str]any
build: Build
}

fn putPlatString(enc: ^jsonenc::Encoder, ps: PlatString) {
Expand Down Expand Up @@ -369,6 +394,62 @@ fn getPlatString(o: any): (PlatString, std::Err) {
return ps, {}
}

fn parseBuild*(o: map[str]any): (Build, std::Err) {
var err: std::Err
b := Build{
isValid: true
}

if validkey(o, "pre") {
b.pre, err = getPlatString(o["pre"])
if err.code != 0 {
return {}, err
}
}

if validkey(o, "post") {
b.post, err = getPlatString(o["post"])
if err.code != 0 {
return {}, err
}
}

if ^[]any(o["include"]) != null {
b.include = []str([]any(o["include"]))
}

if ^map[str]any(o["targets"]) != null {
b.targets = map[str]Target{}
for k, v in map[str]any(o["targets"]) {
vo := map[str]any(v)
t := Target{}
if ^[]any(vo["sources"]) != null {
t.sources = []str([]any(vo["sources"]))
} else if ^str(vo["sources"]) != null {
t.sources = []str{str(vo["sources"])}
}

if validkey(vo, "cflags") {
t.cflags, err = getPlatString(vo["cflags"])
if err.code != 0 {
return {}, err
}
}

if validkey(vo, "ldflags") {
t.ldflags, err = getPlatString(vo["ldflags"])
if err.code != 0 {
return {}, err
}
}

b.targets[k] = t
}
}

return b, {}
}

fn getMeta*(path: str): (Meta, std::Err) {
if !os::isfile(path) {
return {}, error(.fileNotFound, path)
Expand Down Expand Up @@ -516,6 +597,15 @@ fn getMeta*(path: str): (Meta, std::Err) {
}
r ^= delete(r^, "post_build")
}

if ^map[str]any(r["build"]) != null {
m.build, err = parseBuild(map[str]any(r["build"]))
if err.code != 0 {
return {}, err
}

r ^= delete(r^, "build")
}

m.others = r^

Expand Down
222 changes: 222 additions & 0 deletions src/newbuild.um
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@

import (
"std.um"

"../umbox/filepath/filepath.um"
"../umbox/os/os.um"
"../umbox/strings/strings.um"
"../umbox/tar/tar.um"

"common.um"
)

type Params* = struct {
platformTag: bool
winCross: bool
}

type Output* = interface {
addFile(f: str): std::Err
shouldRebuild(target: str, sources: []str): bool
close(): std::Err
cc(file, cmd: str, f: fn(file, cmd: str): std::Err): std::Err
}

type DirOutput* = struct {
path: str
}

fn (o: ^DirOutput) addFile*(p1: str): std::Err {
p2 := filepath::join(o.path, p1)
os::mkdirp(filepath::dir(p2))
f1, err := std::fopen(p1, "rb")
d, err := std::freadall(f1)
f2, err := std::fopen(p2, "wb")
std::fwrite(f2, d)
std::fclose(f1)
std::fclose(f2)

return {} // TODO
}

fn (o: ^DirOutput) shouldRebuild*(target: str, sources: []str): bool {
target = filepath::join(o.path, target)

if !os::isfile(target) {
return true
}

tstat, err := os::stat(target)
if err.code != 0 {
return true
}

for i in sources {
sstat, err := os::stat(sources[i])
if err.code != 0 {
return true
}

if sstat.mtime > tstat.mtime {
return true
}
}

return false
}

fn (o: ^DirOutput) close*(): std::Err {
return {}
}

fn (o: ^DirOutput) cc*(file, cmd: str, f: fn(file, cmd: str): std::Err): std::Err {
return f(filepath::join(o.path, file), cmd)
}

type TarOutput* = struct {
t: tar::Tar
}

fn (o: ^TarOutput) addFile*(f: str): std::Err {
o.t.addFile(f)
// TODO: there is an error in tar.um where Tar.addFile returns incorrect
// error type
return {}
}

fn (o: ^TarOutput) shouldRebuild*(target: str, sources: []str): bool {
return true
}

fn (o: ^TarOutput) close*(): std::Err {
err := o.t.finalize()
if err.code != 0 {
return err
}
return o.t.close()
}

// This might not be the ideal behavior since it might conflict with user intentions.
fn (o: ^TarOutput) cc*(file, cmd: str, f: fn(file, cmd: str): std::Err): std::Err {
err := f(file, cmd)
if err.code != 0 {
return err
}

err = o.addFile(file)
os::remove(file)
return err
}

fn tagTarget(path: str, plat: os::Platform): str {
suffix := ".umi"
switch plat {
case .posix: suffix = "_linux.umi"
case .windows: suffix = "_windows.umi"
case .emscripten: suffix = "_emscripten.umi"
}

return strings::trimsuffix(path, ".umi") + suffix
}

fn compileUMI(out: Output, cc: str, path: str, target: common::Target, plat: os::Platform, par: Params): std::Err {
if par.platformTag {
path = tagTarget(path, os::getPlatform())
}

rebuild := out.shouldRebuild(path, target.sources)
if !rebuild {
if common::debugMode { printf("No need to rebuild %s\n", path) }
return {}
}

command := sprintf("%s -shared %s -o %%s %s %s", cc, target.ldflags.getByPlatform(plat),
target.cflags.getByPlatform(plat), strings::join(target.sources, " "))
return out.cc(path, command, {
cmd = sprintf(cmd, file)
printf("+ %s\n", cmd)
rc := std::system(cmd)
if rc != 0 {
return common::error(.buildError, sprintf("build for %s exited with code %d", file, rc))
}
return {}
})
}

fn buildTargets(out: Output, meta: common::Meta, par: Params): std::Err {
cc := std::getenv("CC")
if cc == "" {
cc = "cc"
}

for path,target in meta.build.targets {
if filepath::ext(path) != "umi" {
printf("Warning: Only .umi files are supported, skipping %s\n", path)
continue
}

err := compileUMI(out, cc, path, target, os::getPlatform(), par)
if err.code != 0 {
return err
}

if (par.winCross) {
err = compileUMI(out, "x86_64-w64-mingw32-gcc", path, target, .windows, par)
if err.code != 0 {
return err
}
}
}

return {}
}

fn run*(out: Output, par: Params): std::Err {
if par.winCross && os::getPlatform() != .posix {
return common::error(.buildError, "Cross compile for Windows only avaiable on Linux")
}

meta, err := common::getMeta("box.json")
if err.code != 0 {
return common::error(.boxJsonError, err.msg)
}

if !meta.build.isValid {
return common::error(.boxJsonError, "no valid build structure")
}

pre := meta.build.pre.get()
if pre != "" {
rc := std::system(pre)
if rc != 0 {
return common::error(.preBuild, sprintf("pre-build exited with code %d", rc))
}
}

err = buildTargets(out, meta, par)
if err.code != 0 {
return err
}

for i,f in meta.build.include {
if os::isfile(f) {
out.addFile(f)
} else if os::isdir(f) {
os::walk(f, fn(p: str) |out| {
out.addFile(p)
})
} else {
return common::error(.fileNotFound, f)
}
}

post := meta.build.post.get()
if post != "" {
rc := std::system(post)
if rc != 0 {
return common::error(.postBuild, sprintf("post-build exited with code %d", rc))
}
}

return {}
}
Loading