Skip to content

Commit

Permalink
kernel: compare mtime's when building source or copying files from …
Browse files Browse the repository at this point in the history
…`initfs`

Closes #66

Enables proper live editing within Wanix! `proc.Spawn` will rebuild commands
from source if it's been modified, and `kernel.fs` will copy in `initfs` files
if they've been modified externally *(note this will overwrite local files
inside Wanix)*.
  • Loading branch information
Parzival-3141 committed Feb 7, 2024
1 parent 941281c commit 42dcc87
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 31 deletions.
10 changes: 5 additions & 5 deletions dev/bootloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ if (!globalThis["ServiceWorkerGlobalScope"]) {
const load = async (name, file) => {
// Determine if file contains a path to fetch or embedded file contents to load.
if(file.type === "text/plain") {
globalThis.initfs[name] = await (await fetch(`./sys/dev/${file.data}`)).blob();
globalThis.initfs[name] = { mtimeMs: file.mtimeMs, blob: await (await fetch(`./sys/dev/${file.data}`)).blob() };
} else {
globalThis.initfs[name] = await (await fetch(`./~init/${name}`)).blob();
globalThis.initfs[name] = { mtimeMs: file.mtimeMs, blob: await (await fetch(`./~init/${name}`)).blob() };
}
};

Expand All @@ -97,8 +97,8 @@ if (!globalThis["ServiceWorkerGlobalScope"]) {
}
await Promise.all(loads);

globalThis.duplex = await import(URL.createObjectURL(initfs["duplex.js"]));
globalThis.task = await import(URL.createObjectURL(initfs["task.js"]));
globalThis.duplex = await import(URL.createObjectURL(initfs["duplex.js"].blob));
globalThis.task = await import(URL.createObjectURL(initfs["task.js"].blob));

globalThis.sys = new task.Task(initfs);

Expand All @@ -107,7 +107,7 @@ if (!globalThis["ServiceWorkerGlobalScope"]) {
await sys.exec("kernel");

// load host API
await import(URL.createObjectURL(initfs["host.js"]));
await import(URL.createObjectURL(initfs["host.js"].blob));

})();
}
Expand Down
18 changes: 13 additions & 5 deletions dev/initdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var files = []File{
{Name: "build", Path: "./local/bin/build"},
{Name: "macro", Path: "./local/bin/micro"},

// {Name: "shell", Path: "./local/bin/shell"},
// Shell source files
{Name: "shell/main.go", Path: "shell/main.go"},
{Name: "shell/copy.go", Path: "shell/copy.go"},
{Name: "shell/download.go", Path: "shell/download.go"},
Expand Down Expand Up @@ -54,6 +54,10 @@ func PackFilesTo(w io.Writer, mode PackMode) {
files[i].Type = "application/octet-stream"
}

fi, err := os.Stat(files[i].Path)
fatal(err)
files[i].Mtime = fi.ModTime().UnixMilli()

data, err := os.ReadFile(files[i].Path)
fatal(err)
var gzipBuffer bytes.Buffer
Expand All @@ -68,6 +72,9 @@ func PackFilesTo(w io.Writer, mode PackMode) {
for i := range files {
files[i].Type = "text/plain"
files[i].Data = files[i].Path
fi, err := os.Stat(files[i].Path)
fatal(err)
files[i].Mtime = fi.ModTime().UnixMilli()
}
}

Expand All @@ -78,10 +85,11 @@ func PackFilesTo(w io.Writer, mode PackMode) {
}

type File struct {
Name string
Path string
Type string
Data string
Name string
Path string
Type string
Data string
Mtime int64
}

func fatal(err error) {
Expand Down
2 changes: 1 addition & 1 deletion dev/initdata.tmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
globalThis.initdata = {
{{range .}}
"{{.Name}}": {type: "{{.Type}}", data: "{{.Data}}"},
"{{.Name}}": {type: "{{.Type}}", mtimeMs: {{.Mtime}}, data: "{{.Data}}"},
{{end}}
}
2 changes: 1 addition & 1 deletion internal/indexedfs/indexedfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type FS struct {

func New() (*FS, error) {
if helper.IsUndefined() {
blob := js.Global().Get("initfs").Get("indexedfs.js")
blob := js.Global().Get("initfs").Get("indexedfs.js").Get("blob")
url := js.Global().Get("URL").Call("createObjectURL", blob)
helper = jsutil.Await(js.Global().Call("import", url))
}
Expand Down
16 changes: 7 additions & 9 deletions kernel/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ func getPrefixedInitFiles(prefix string) []string {
}

func (s *Service) copyFileFromInitFS(dst, src string) error {
initFile := js.Global().Get("initfs").Get(src)
if initFile.IsUndefined() {
return nil
}

var exists bool
fi, err := fs.Stat(s.fsys, dst)
if err == nil {
Expand All @@ -122,15 +127,8 @@ func (s *Service) copyFileFromInitFS(dst, src string) error {
return err
}

blob := js.Global().Get("initfs").Get(src)
if blob.IsUndefined() {
return nil
}

// TODO: use better heuristic. Editing a file will trigger this code and
// overwrite any changes. This is most definitely unintended.
// TODO: store the mtime for initfs files and compare them with the wanix mtime.
if !exists || int64(blob.Get("size").Int()) != fi.Size() {
if !exists || time.UnixMilli(int64(initFile.Get("mtimeMs").Float())).After(fi.ModTime()) {
blob := initFile.Get("blob")
buffer, err := jsutil.AwaitErr(blob.Call("arrayBuffer"))
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion kernel/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type Kernel struct {

func (k *Kernel) Run(ctx context.Context) error {
// import syscall.js
blob := js.Global().Get("initfs").Get("syscall.js")
blob := js.Global().Get("initfs").Get("syscall.js").Get("blob")
url := js.Global().Get("URL").Call("createObjectURL", blob)
jsutil.Await(js.Global().Call("import", url))

Expand Down
42 changes: 39 additions & 3 deletions kernel/proc/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,45 @@ func unixToFsPath(path string) string {
func (s *Service) buildCmdSource(path, workingDir string) (wasmPath string, err error) {
wasmPath = filepath.Join("/sys/bin", filepath.Base(path)+".wasm")

// TODO: use mtime and ctime to determine if cmd needs rebuilding.
wasmExists, err := fs.Exists(os.DirFS("/"), unixToFsPath(wasmPath))
if err != nil {
dfs := os.DirFS("/")
var wasmExists bool
wasmStat, err := fs.Stat(dfs, unixToFsPath(wasmPath))
if err == nil {
wasmExists = true
} else if os.IsNotExist(err) {
wasmExists = false
} else {
return "", err
}

var shouldBuild bool
if !wasmExists {
shouldBuild = true
} else {
wasmMtime := wasmStat.ModTime()
err = fs.WalkDir(dfs, unixToFsPath(path), func(walkPath string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}

fi, err := d.Info()
if err != nil {
return err
}

if fi.ModTime().After(wasmMtime) {
shouldBuild = true
return fs.SkipAll
}

return nil
})
if err != nil {
return "", err
}
}

if shouldBuild {
p, err := s.Spawn(
"/sys/cmd/build.wasm",
[]string{"-output", wasmPath, path},
Expand All @@ -152,6 +184,10 @@ func (s *Service) buildCmdSource(path, workingDir string) (wasmPath string, err
return "", err
}

// TODO: https://github.com/tractordev/wanix/issues/69
// go io.Copy(os.Stdout, p.Stdout())
// go io.Copy(os.Stderr, p.Stderr())

exitCode, err := p.Wait()
if exitCode != 0 {
return "", err
Expand Down
6 changes: 3 additions & 3 deletions kernel/web/lib/task.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions kernel/web/lib/worker.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 42dcc87

Please sign in to comment.