initial commit

This commit is contained in:
Giacomo Tesio 2015-12-22 10:10:07 +00:00
commit 0874dc3613
28 changed files with 3030 additions and 0 deletions

39
QA.sh Executable file
View File

@ -0,0 +1,39 @@
#!/bin/bash
trap : 2
$JEHANNE/hacking/bin/ufs -root=$JEHANNE &
ufspid=$!
export machineflag=pc
if [ "$(uname)" = "Linux" ] && [ -e /dev/kvm ]; then
export kvmflag='-enable-kvm'
export machineflag='pc,accel=kvm'
if [ ! -w /dev/kvm ]; then
# we don't have access as a regular user
export kvmdo=sudo
fi
fi
cd $JEHANNE/arch/$ARCH/kern/
read -r cmd <<EOF
$kvmdo qemu-system-x86_64 -s -cpu Opteron_G1 -smp 1 -m 2048 $kvmflag \
-serial stdio \
--nographic \
--monitor /dev/null \
--machine $machineflag \
-net nic,model=rtl8139 \
-net user,hostfwd=tcp::5555-:1522 \
-net dump,file=/tmp/vm0.pcap \
-redir tcp:9999::9 \
-redir tcp:17010::17010 \
-redir tcp:17013::17013 \
-append "nobootprompt=tcp maxcores=1024 fs=10.0.2.2 auth=10.0.2.2 nvram=/boot/nvram nvrlen=512 nvroff=0" \
-kernel jehanne.32bit $*
EOF
echo $cmd
eval $cmd
kill $ufspid
wait

2
bin/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

10
buildtools.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/sh
cd `dirname $0`
UTILITIES=`pwd`
git clean -x -d -f $UTILITIES
echo -n Building development tools...
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go get -d jehanne/cmd/...
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go install jehanne/cmd/...
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go install github.com/lionkov/ninep/srv/examples/ufs
echo " DONE."

22
continuous-build.sh Executable file
View File

@ -0,0 +1,22 @@
#!/bin/bash
set -e
if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then
export JEHANNE=`git rev-parse --show-toplevel|sed 's/\/hacking//g'`
export PATH="$JEHANNE/hacking/bin:$PATH"
export SH=`which rc`
export ARCH=amd64
git clean -x -d -f
(cd $JEHANNE && ./hacking/buildtools.sh)
echo
echo "Vendorized code verification..."
echo
for v in `find $JEHANNE -type f|grep vendor.json`; do
echo "cd `dirname $v`"
(cd `dirname $v`; vendor -check)
done
echo
build all
fi

14
coverity-scan.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
set -e
if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then
export JEHANNE=`git rev-parse --show-toplevel|sed 's/\/hacking//g'`
export PATH="$JEHANNE/hacking:$PATH"
export SH=`which rc`
export ARCH=amd64
git clean -x -d -f
(cd $JEHANNE && ./hacking/buildtools.sh)
cd $JEHANNE
build
fi

6
devshell.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
export JEHANNE=`git rev-parse --show-toplevel`
export PATH="$JEHANNE/hacking/bin:$PATH"
export ARCH=amd64
bash --rcfile <(cat ~/.bashrc; echo 'PS1="JehanneDEV $PS1"')

BIN
nvram Normal file

Binary file not shown.

38
runOver9P.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh
trap : 2
$JEHANNE/hacking/bin/ufs -root=$JEHANNE &
ufspid=$!
export machineflag=pc
if [ "$(uname)" = "Linux" ] && [ -e /dev/kvm ]; then
export kvmflag='-enable-kvm'
export machineflag='pc,accel=kvm'
if [ ! -w /dev/kvm ]; then
# we don't have access as a regular user
export kvmdo=sudo
fi
fi
cd $JEHANNE/arch/$ARCH/kern/
read -r cmd <<EOF
$kvmdo qemu-system-x86_64 -s -cpu Opteron_G1 -smp 1 -m 2048 $kvmflag \
-serial stdio \
--machine $machineflag \
-net nic,model=rtl8139 \
-net user,hostfwd=tcp::5555-:1522 \
-net dump,file=/tmp/vm0.pcap \
-redir tcp:9999::9 \
-redir tcp:17010::17010 \
-redir tcp:17013::17013 \
-append "nobootprompt=tcp maxcores=1024 fs=10.0.2.2 auth=10.0.2.2 nvram=/boot/nvram nvrlen=512 nvroff=0" \
-kernel jehanne.32bit $*
EOF
echo $cmd
eval $cmd
kill $ufspid
wait

View File

@ -0,0 +1,541 @@
// Build builds code as directed by json files.
// We slurp in the JSON, and recursively process includes.
// At the end, we issue a single cc command for all the files.
// Compilers are fast.
//
// ENVIRONMENT
//
// Needed: JEHANNE, ARCH
//
// JEHANNE should point to a Jehanne root.
// Currently only "amd64" is a valid ARCH.
// A best-effort to autodetect the Jehanne root is made if not explicitly set.
//
// Optional: CC, AR, LD, RANLIB, STRIP, SH, TOOLPREFIX
//
// These all control how the needed tools are found.
//
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strings"
)
type kernconfig struct {
Code []string
Dev []string
Ip []string
Link []string
Sd []string
Uart []string
VGA []string
}
type kernel struct {
Systab string
Config kernconfig
Ramfiles map[string]string
}
type build struct {
// jsons is unexported so can not be set in a .json file
jsons map[string]bool
path string
name string
// Projects name a whole subproject which is built independently of
// this one. We'll need to be able to use environment variables at some point.
Projects []string
Pre []string
Post []string
Cflags []string
Oflags []string
Include []string
SourceFiles []string
ObjectFiles []string
Libs []string
Env []string
// cmd's
SourceFilesCmd []string
// Targets.
Program string
Library string
Install string // where to place the resulting binary/lib
Kernel *kernel
}
type buildfile map[string]build
// UnmarshalJSON works like the stdlib unmarshal would, except it adjusts all
// paths.
func (bf *buildfile) UnmarshalJSON(s []byte) error {
r := make(map[string]build)
if err := json.Unmarshal(s, &r); err != nil {
return err
}
for k, b := range r {
// we're getting a copy of the struct, remember.
b.jsons = make(map[string]bool)
b.Projects = adjust(b.Projects)
b.Libs = adjust(b.Libs)
b.Cflags = adjust(b.Cflags)
b.SourceFiles = adjust(b.SourceFiles)
b.SourceFilesCmd = adjust(b.SourceFilesCmd)
b.ObjectFiles = adjust(b.ObjectFiles)
b.Include = adjust(b.Include)
b.Install = fromRoot(b.Install)
for i, e := range b.Env {
b.Env[i] = os.ExpandEnv(e)
}
r[k] = b
}
*bf = r
return nil
}
var (
cwd string
jehanne string
regexpAll = []*regexp.Regexp{regexp.MustCompile(".")}
// findTools looks at all env vars and absolutizes these paths
// also respects TOOLPREFIX
tools = map[string]string{
"cc": "gcc",
"ar": "ar",
"ld": "ld",
"ranlib": "ranlib",
"strip": "strip",
"sh": "sh",
}
arch = map[string]bool{
"amd64": true,
}
debugPrint = flag.Bool("debug", false, "enable debug prints")
shellhack = flag.Bool("shellhack", false, "spawn every command in a shell (forced on if LD_PRELOAD is set)")
)
func debug(fmt string, s ...interface{}) {
if *debugPrint {
log.Printf(fmt, s...)
}
}
// fail with message, if err is not nil
func failOn(err error) {
if err != nil {
log.Fatalf("%v\n", err)
}
}
func adjust(s []string) []string {
for i, v := range s {
s[i] = fromRoot(v)
}
return s
}
// return the given absolute path as an absolute path rooted at the jehanne tree.
func fromRoot(p string) string {
p = os.ExpandEnv(p)
if path.IsAbs(p) {
return path.Join(jehanne, p)
}
return p
}
// Sh sends cmd to a shell. It's needed to enable $LD_PRELOAD tricks,
// see https://github.com/Harvey-OS/jehanne/issues/8#issuecomment-131235178
func sh(cmd *exec.Cmd) {
shell := exec.Command(tools["sh"])
shell.Env = cmd.Env
if cmd.Args[0] == tools["sh"] && cmd.Args[1] == "-c" {
cmd.Args = cmd.Args[2:]
}
commandString := strings.Join(cmd.Args, " ")
if shStdin, e := shell.StdinPipe(); e == nil {
go func() {
defer shStdin.Close()
io.WriteString(shStdin, commandString)
}()
} else {
log.Fatalf("cannot pipe [%v] to %s: %v", commandString, tools["sh"], e)
}
shell.Stderr = os.Stderr
shell.Stdout = os.Stdout
debug("%q | sh\n", commandString)
failOn(shell.Run())
}
func include(f string, b *build) {
if b.jsons[f] {
return
}
b.jsons[f] = true
log.Printf("Including %v", f)
d, err := ioutil.ReadFile(f)
failOn(err)
var builds buildfile
failOn(json.Unmarshal(d, &builds))
for n, build := range builds {
log.Printf("Merging %v", n)
b.SourceFiles = append(b.SourceFiles, build.SourceFiles...)
b.Cflags = append(b.Cflags, build.Cflags...)
b.Oflags = append(b.Oflags, build.Oflags...)
b.Pre = append(b.Pre, build.Pre...)
b.Post = append(b.Post, build.Post...)
b.Libs = append(b.Libs, build.Libs...)
b.Projects = append(b.Projects, build.Projects...)
b.Env = append(b.Env, build.Env...)
b.SourceFilesCmd = append(b.SourceFilesCmd, build.SourceFilesCmd...)
b.Program += build.Program
b.Library += build.Library
if build.Install != "" {
if b.Install != "" {
log.Fatalf("In file %s (target %s) included by %s (target %s): redefined Install.", f, n, build.path, build.name)
}
b.Install = build.Install
}
b.ObjectFiles = append(b.ObjectFiles, build.ObjectFiles...)
// For each source file, assume we create an object file with the last char replaced
// with 'o'. We can get smarter later.
for _, v := range build.SourceFiles {
f := path.Base(v)
o := f[:len(f)-1] + "o"
b.ObjectFiles = append(b.ObjectFiles, o)
}
for _, v := range build.Include {
if !path.IsAbs(v) {
wd := path.Dir(f)
v = path.Join(wd, v)
}
include(v, b)
}
}
}
func appendIfMissing(s []string, v string) []string {
for _, a := range s {
if a == v {
return s
}
}
return append(s, v)
}
func process(f string, r []*regexp.Regexp) []build {
log.Printf("Processing %v", f)
var builds buildfile
var results []build
d, err := ioutil.ReadFile(f)
failOn(err)
failOn(json.Unmarshal(d, &builds))
for n, build := range builds {
build.name = n
build.jsons = make(map[string]bool)
skip := true
for _, re := range r {
if re.MatchString(build.name) {
skip = false
break
}
}
if skip {
continue
}
log.Printf("Run %v", build.name)
build.jsons[f] = true
build.path = path.Dir(f)
// For each source file, assume we create an object file with the last char replaced
// with 'o'. We can get smarter later.
for _, v := range build.SourceFiles {
f := path.Base(v)
o := f[:len(f)-1] + "o"
build.ObjectFiles = appendIfMissing(build.ObjectFiles, o)
}
for _, v := range build.Include {
include(v, &build)
}
results = append(results, build)
}
return results
}
func buildkernel(b *build) {
if b.Kernel == nil {
return
}
codebuf := confcode(b.path, b.Kernel)
failOn(ioutil.WriteFile(b.name+".c", codebuf, 0666))
}
func wrapInQuote(args []string) []string {
var res []string
for _, a := range(args){
if strings.Contains(a, "=") {
res = append(res, "'" + a + "'")
} else {
res = append(res, a)
}
}
return res
}
func compile(b *build) {
log.Printf("Building %s\n", b.name)
// N.B. Plan 9 has a very well defined include structure, just three things:
// /amd64/include, /sys/include, .
args := b.Cflags
if len(b.SourceFilesCmd) > 0 {
for _, i := range b.SourceFilesCmd {
cmd := exec.Command(tools["cc"], append(args, i)...)
run(b, *shellhack, cmd)
}
return
}
args = append(args, b.SourceFiles...)
cmd := exec.Command(tools["cc"], args...)
run(b, *shellhack, cmd)
}
func link(b *build) {
log.Printf("Linking %s\n", b.name)
if len(b.SourceFilesCmd) > 0 {
for _, n := range b.SourceFilesCmd {
// Split off the last element of the file
var ext = filepath.Ext(n)
if len(ext) == 0 {
log.Fatalf("refusing to overwrite extension-less source file %v", n)
continue
}
n = n[:len(n)-len(ext)]
f := path.Base(n)
o := f[:len(f)] + ".o"
args := []string{"-o", n, o}
args = append(args, b.Oflags...)
args = append(args, "-L", fromRoot("/arch/$ARCH/lib"))
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
return
}
args := []string{"-o", b.Program}
args = append(args, b.ObjectFiles...)
args = append(args, b.Oflags...)
args = append(args, "-L", fromRoot("/arch/$ARCH/lib"))
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
func install(b *build) {
if b.Install == "" {
return
}
log.Printf("Installing %s\n", b.name)
failOn(os.MkdirAll(b.Install, 0755))
switch {
case len(b.SourceFilesCmd) > 0:
for _, n := range b.SourceFilesCmd {
ext := filepath.Ext(n)
exe := n[:len(n)-len(ext)]
move(exe, b.Install)
}
case len(b.Program) > 0:
move(b.Program, b.Install)
case len(b.Library) > 0:
libpath := path.Join(b.Install, b.Library)
args := append([]string{"-rs", libpath}, b.ObjectFiles...)
run(b, *shellhack, exec.Command(tools["ar"], args...))
run(b, *shellhack, exec.Command(tools["ranlib"], libpath))
}
}
func move(from, to string) {
final := path.Join(to, from)
log.Printf("move %s %s\n", from, final)
_ = os.Remove(final)
failOn(os.Link(from, final))
failOn(os.Remove(from))
}
func run(b *build, pipe bool, cmd *exec.Cmd) {
if b != nil {
cmd.Env = append(os.Environ(), b.Env...)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if pipe {
// Sh sends cmd to a shell. It's needed to enable $LD_PRELOAD tricks, see https://github.com/Harvey-OS/jehanne/issues/8#issuecomment-131235178
shell := exec.Command(tools["sh"])
shell.Env = cmd.Env
shell.Stderr = os.Stderr
shell.Stdout = os.Stdout
commandString := cmd.Args[0]
commandString += " " + strings.Join(wrapInQuote(cmd.Args[1:]), " ")
shStdin, err := shell.StdinPipe()
if err != nil {
log.Fatalf("cannot pipe [%v] to %s: %v", commandString, tools["sh"], err)
}
go func() {
defer shStdin.Close()
io.WriteString(shStdin, commandString)
}()
log.Printf("%q | %s\n", commandString, tools["sh"])
failOn(shell.Run())
return
}
log.Println(strings.Join(cmd.Args, " "))
failOn(cmd.Run())
}
func projects(b *build, r []*regexp.Regexp) {
for _, v := range b.Projects {
f, _ := findBuildfile(v)
log.Printf("Doing %s\n", f)
project(f, r)
}
}
// assumes we are in the wd of the project.
func project(bf string, which []*regexp.Regexp) {
cwd, err := os.Getwd()
failOn(err)
debug("Start new project cwd is %v", cwd)
defer os.Chdir(cwd)
dir := path.Dir(bf)
root := path.Base(bf)
debug("CD to %v and build using %v", dir, root)
failOn(os.Chdir(dir))
builds := process(root, which)
debug("Processing %v: %d target", root, len(builds))
for _, b := range builds {
debug("Processing %v: %v", b.name, b)
projects(&b, regexpAll)
for _, c := range b.Pre {
// this is a hack: we just pass the command through as an exec.Cmd
run(&b, true, exec.Command(c))
}
buildkernel(&b)
if len(b.SourceFiles) > 0 || len(b.SourceFilesCmd) > 0 {
compile(&b)
}
if b.Program != "" || len(b.SourceFilesCmd) > 0 {
link(&b)
}
install(&b)
for _, c := range b.Post {
run(&b, true, exec.Command(c))
}
}
}
func main() {
// A small amount of setup is done in the paths*.go files. They are
// OS-specific path setup/manipulation. "jehanne" is set there and $PATH is
// adjusted.
var err error
findTools(os.Getenv("TOOLPREFIX"))
flag.Parse()
cwd, err = os.Getwd()
failOn(err)
a := os.Getenv("ARCH")
if a == "" || !arch[a] {
s := []string{}
for i := range arch {
s = append(s, i)
}
log.Fatalf("You need to set the ARCH environment variable from: %v", s)
}
// ensure this is exported, in case we used a default value
os.Setenv("JEHANNE", jehanne)
if os.Getenv("LD_PRELOAD") != "" {
log.Println("Using shellhack")
*shellhack = true
}
// If no args, assume 'build.json'
// Otherwise the first argument is either
// - the path to a json file
// - a directory containing a 'build.json' file
// - a regular expression to apply assuming 'build.json'
// Further arguments are regular expressions.
consumedArgs := 0;
bf := ""
if len(flag.Args()) == 0 {
f, err := findBuildfile("build.json")
failOn(err)
bf = f
} else {
f, err := findBuildfile(flag.Arg(0))
failOn(err)
if f == "" {
f, err := findBuildfile("build.json")
failOn(err)
bf = f
} else {
consumedArgs = 1
bf = f
}
}
re := []*regexp.Regexp{regexp.MustCompile(".")}
if len(flag.Args()) > consumedArgs {
re = re[:0]
for _, r := range flag.Args()[consumedArgs:] {
rx, err := regexp.Compile(r)
failOn(err)
re = append(re, rx)
}
}
project(bf, re)
}
func findTools(toolprefix string) {
var err error
for k, v := range tools {
if x := os.Getenv(strings.ToUpper(k)); x != "" {
v = x
}
v, err = exec.LookPath(toolprefix + v)
failOn(err)
tools[k] = v
}
}
// disambiguate the buildfile argument
func findBuildfile(f string) (string, error) {
if strings.HasSuffix(f, ".json"){
if fi, err := os.Stat(f); err == nil && !fi.IsDir() {
return f, nil
}
return "", fmt.Errorf("unable to find buildfile %s", f)
}
if strings.Contains(f, "/") {
return findBuildfile(path.Join(f, "build.json"))
}
return "", nil
}

View File

@ -0,0 +1,196 @@
package main
import (
"bytes"
"debug/elf"
"fmt"
"io/ioutil"
"os"
"os/exec"
"text/template"
)
const kernconfTmpl = `
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
void
rdb(void)
{
splhi();
iprint("rdb...not installed\n");
for(;;);
}
{{ range .Rootcodes }}
{{ . }}
{{ end }}
{{ range .Config.Dev }}extern Dev {{ . }}devtab;
{{ end }}
Dev *devtab[] = {
{{ range .Config.Dev }}
&{{ . }}devtab,
{{ end }}
nil,
};
{{ range .Config.Link }}extern void {{ . }}link(void);
{{ end }}
void
links(void)
{
{{ range .Rootnames }}addbootfile("{{ . }}", ramfs_{{ . }}_code, ramfs_{{ . }}_len);
{{ end }}
{{ range .Config.Link }}{{ . }}link();
{{ end }}
}
#include "../ip/ip.h"
{{ range .Config.Ip }}extern void {{ . }}init(Fs*);
{{ end }}
void (*ipprotoinit[])(Fs*) = {
{{ range .Config.Ip }} {{ . }}init,
{{ end }}
nil,
};
#include "../port/sd.h"
{{ range .Config.Sd }}extern SDifc {{ . }}ifc;
{{ end }}
SDifc* sdifc[] = {
{{ range .Config.Sd }} &{{ . }}ifc,
{{ end }}
nil,
};
{{ range .Config.Uart }}extern PhysUart {{ . }}physuart;
{{ end }}
PhysUart* physuart[] = {
{{ range .Config.Uart }} &{{ . }}physuart,
{{ end }}
nil,
};
#define Image IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"
{{ range .Config.VGA }}extern VGAdev {{ . }}dev;
{{ end }}
VGAdev* vgadev[] = {
{{ range .Config.VGA }} &{{ . }}dev,
{{ end }}
nil,
};
{{ range .Config.VGA }}extern VGAcur {{ . }}cur;
{{ end }}
VGAcur* vgacur[] = {
{{ range .Config.VGA }} &{{ . }}cur,
{{ end }}
nil,
};
Physseg physseg[8] = {
{
.attr = SG_SHARED,
.name = "shared",
.size = SEGMAXPG,
},
{
.attr = SG_BSS,
.name = "memory",
.size = SEGMAXPG,
},
};
int nphysseg = 8;
{{ range .Config.Code }}{{ . }}
{{ end }}
char* conffile = "{{ .Path }}";
`
// These are the two big code generation functions.
// data2c takes the file at path and creates a C byte array containing it.
func data2c(name string, path string) string {
var out []byte
var in []byte
if elf, err := elf.Open(path); err == nil {
elf.Close()
cwd, err := os.Getwd()
tmpf, err := ioutil.TempFile(cwd, name)
failOn(err)
run(nil, *shellhack, exec.Command(tools["strip"], "-o", tmpf.Name(), path))
in, err = ioutil.ReadAll(tmpf)
failOn(err)
tmpf.Close()
os.Remove(tmpf.Name())
} else {
var file *os.File
var err error
file, err = os.Open(path)
failOn(err)
in, err = ioutil.ReadAll(file)
failOn(err)
file.Close()
}
total := len(in)
out = []byte(fmt.Sprintf("static unsigned char ramfs_%s_code[] = {\n", name))
for len(in) > 0 {
for j := 0; j < 16 && len(in) > 0; j++ {
out = append(out, []byte(fmt.Sprintf("0x%02x, ", in[0]))...)
in = in[1:]
}
out = append(out, '\n')
}
out = append(out, []byte(fmt.Sprintf("0,\n};\nint ramfs_%s_len = %v;\n", name, total))...)
return string(out)
}
// confcode creates a kernel configuration header.
func confcode(path string, kern *kernel) []byte {
var rootcodes []string
var rootnames []string
for name, path := range kern.Ramfiles {
code := data2c(name, fromRoot(path))
rootcodes = append(rootcodes, code)
rootnames = append(rootnames, name)
}
vars := struct {
Path string
Config kernconfig
Rootnames []string
Rootcodes []string
}{
path,
kern.Config,
rootnames,
rootcodes,
}
tmpl := template.Must(template.New("kernconf").Parse(kernconfTmpl))
codebuf := &bytes.Buffer{}
failOn(tmpl.Execute(codebuf, vars))
return codebuf.Bytes()
}

View File

@ -0,0 +1,184 @@
/*
BUILDFILE FORMAT
A buildfile is a json object containing build objects. By convention, it's
broken out onto multiple lines indented by tabs, and the keys are TitleCased.
The environment varibles JEHANNE and ARCH are guarenteed to be set for the
buildfile. PATH is modified so that "$JEHANNE/hacking" is included.
Any key that takes a path or array of paths as its value has absolute paths
re-rooted to the jehanne tree and variables in the string expanded once.
The shell commands in the "Pre" and "Post" steps must be in the subset of
syntax that's accepted by both POSIX sh and rc. Practically, this means
arguments with a "=" must be single-quoted, "test" must be called as
"test" (not "["), "if" statements may not have an "else" clause, "switch"
statements may not be used, "for" statements may only have one body command,
and redirection to a file descriptor cannont be used.
BUILD OBJECT
A build object has the following keys and types:
Projects []string
Pre []string
Post []string
Cflags []string
Oflags []string
Include []string
SourceFiles []string
ObjectFiles []string
Libs []string
Env []string
SourceFilesCmd []string
Program string
Library string
Install string
Kernel kernel
These are the steps taken, in order:
Env
Include
Projects
Pre
Kernel
[compile] SourceFiles SourceFilesCmd Cflags Program
[link] ObjectFiles Libs Oflags Library
Install
Post
"[compile]" and "[link]" are steps synthesized from the specified keys.
The meaning of the keys is as follows:
"Env" is an array of environment variables to be put in the environment of
every command run in a build. They are expanded once and only available for
use in other steps.
"Include" is an array of buildfiles to "merge" into the current one. This
is done before other projects are built.
"Projects" is an array of buildfiles to build in their entirety before
starting the current build.
"Pre" is an array of commands to run before starting the build.
"Kernel" is a kernel build object. See the kernel object section.
"Cflags" is an array of flags to pass to the C compiler. They are in addition
to the standard flags of
-std=c11 -c -I /$ARCH/include -I /sys/include -I .
The standard include paths are re-rooted to the jehanne tree if not on a jehanne
system.
"SourceFilesCmd" is an array of C files where each one should result in an
executable. If this key is provided, "SourceFiles" and "Program" are ignored.
"SourceFiles" is an array of C files that will ultimately produce a single
binary or library, named by "Program" or "Library" respectively.
"Program" is the name of the final executable for the files specified by
"SourceFiles".
"Oflags" is an array of flags to pass to the linker. They are in addition
to the standard flags of
-o $program $objfiles -L /$ARCH/lib $libs
The lib path is re-rooted to the jehanne tree if not on a jehanne system.
"ObjectFiles" is an array of strings specifying object files to be linked
into the final program specified by "Program". Any object files produced
by the preceeding "[compile]" step are automatically added to this before
beginning the "[link]" step.
"Libs" is a an array of library arguments to pass to the linker.
"Library" is the name of the archive resulting from bundling "SourceFiles"
into a library. The resulting archive has 'ranlib' run on it automatically.
"Install" is a directory where the result of the "[link]" step is moved
to. If it does not exist, it is created.
"Post" is an array of commands to run last during the build.
KERNEL OBJECT
A build object has the following keys and types:
Systab string
Ramfiles map[string]string
Config {
Code []string
Dev []string
Ip []string
Link []string
Sd []string
Uart []string
VGA []string
}
"Systab" is the header that defines the syscall table.
"Ramfiles" is an object of name, path pairs of binaries at "path" that will
be baked into the kernel and available at a binary named "name".
"Config" is an object used to generate the kernel configuration source. "Dev",
"Ip", "Sd", "Uart", "Link", and "VGA" control which drivers of the various
types are included. "Code" is lines of arbitrary C code.
*/
package main
import (
"flag"
"fmt"
"os"
"strings"
)
var helptext = `
The buildfile is looked for at these positions, in this order:
./$arg
./$arg/build.json
/sys/src/$arg.json
/sys/src/$arg/build.json
If the buildfile argument is not provided, it defaults to "build.json".
After the buildfile, a number of regexps specifying targets may be provided.
If a target matches any supplied regexp, it is acted on. These regexps only
apply to the top-level buildfile.
BUILDFILE
See the build godoc for more information about the buildfile format.
ENVIRONMENT
ARCH is needed. Current acceptable vaules are: amd64
JEHANNE may be supplied to point at a jehanne tree.
The default on Jehanne is "/".
The default on Linux and OSX is to attempt to find the top level of a git
repository.
`
func init() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s [options] [buildfile] [target...]\n\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, helptext)
fmt.Fprintln(os.Stderr, "Tools to be used with current settings:")
fmt.Fprintf(os.Stderr, " prefix ($TOOLPREFIX): %q\n", os.Getenv("TOOLPREFIX"))
for k, v := range tools {
fmt.Fprintf(os.Stderr, " %s ($%s): %s\n", k, strings.ToUpper(k), v)
}
}
}

View File

@ -0,0 +1,31 @@
// +build !jehanne
package main
import (
"log"
"os"
"os/exec"
"strings"
)
func init() {
jehanne = os.Getenv("JEHANNE")
if jehanne != "" {
return
}
// git is purely optional, for lazy people.
out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err == nil {
jehanne = strings.TrimSpace(string(out))
hackingAt := strings.LastIndex("/hacking", jehanne)
if(hackingAt >= 0){
jehanne = jehanne[0:hackingAt]
}
}
if jehanne == "" {
log.Fatal("Set the JEHANNE environment variable or run from a git checkout.")
}
os.Setenv("PATH", strings.Join([]string{fromRoot("/hacking"), os.Getenv("PATH")}, string(os.PathListSeparator)))
}

View File

@ -0,0 +1,17 @@
// +build jehanne
package main
import (
"os"
"strings"
)
func init() {
jehanne = os.Getenv("JEHANNE")
if jehanne != "" {
return
}
jehanne = "/"
os.Setenv("path", strings.Join([]string{fromRoot("/util"), os.Getenv("path")}, string(os.PathListSeparator)))
}

View File

@ -0,0 +1,155 @@
package main
import (
"encoding/json"
"flag"
"io/ioutil"
"log"
"os"
"path/filepath"
)
var config = struct {
Jehanne string
Args []string
Except map[string]bool
CmdName string
FullPath string
Src string
Uroot string
Cwd string
Bbsh string
Goroot string
Gosrcroot string
Arch string
Goos string
Gopath string
TempDir string
Go string
Debug bool
Fail bool
}{
Jehanne: os.Getenv("JEHANNE"),
Except: map[string]bool{"sysconf.json": true},
}
type fixup func(string, map[string]interface{})
func cflags(n string, jsmap map[string]interface{}) {
if _, ok := jsmap["Cflags"]; ok {
log.Printf("Deleting Cflags from %v", n)
delete(jsmap, "Cflags")
// TODO: once we have another ARCH, use it.
a := []string{"/arch/amd64/include/cflags.json"}
switch tval := jsmap["Include"].(type) {
case []interface{}:
for _, v := range tval {
a = append(a, v.(string))
}
}
jsmap["Include"] = a
}
}
func removeempty(n string, jsmap map[string]interface{}) {
for key, val := range jsmap {
switch tval := val.(type) {
case map[string]interface{}:
log.Printf("%s: tval %s", n, tval)
if len(tval) == 0 {
delete(jsmap, key)
}
case []interface{}:
if len(tval) == 0 {
delete(jsmap, key)
}
}
}
}
func checkname(n string, jsmap map[string]interface{}) {
if _, ok := jsmap["Name"]; !ok {
log.Print("File %v has no \"Name\" key", n)
}
}
func one(n string, f ...fixup) error {
buf, err := ioutil.ReadFile(n)
if err != nil {
return err
}
var jsmap map[string]interface{}
if err := json.Unmarshal(buf, &jsmap); err != nil {
return err
}
if config.Debug {
log.Printf("%v: %v", n, jsmap)
}
mapname := jsmap["Name"].(string)
delete(jsmap, "Name")
var nmap = make(map[string]map[string]interface{})
nmap[mapname] = jsmap
buf, err = json.MarshalIndent(nmap, "", "\t")
if err != nil {
return err
}
buf = append(buf, '\n')
if config.Debug {
os.Stdout.Write(buf)
} else {
ioutil.WriteFile(n, buf, 0666)
}
return nil
}
func init() {
if config.Jehanne == "" {
log.Fatalf("Please set $JEHANNE")
}
if len(config.Args) == 0 {
config.Args = []string{config.Jehanne}
}
}
func main() {
var err error
flag.BoolVar(&config.Debug, "d", true, "Enable debug prints")
flag.Parse()
config.Args = flag.Args()
for _, n := range config.Args {
err = filepath.Walk(n, func(name string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
return nil
}
n := fi.Name()
if len(n) < 5 || n[len(n)-5:] != ".json" {
return nil
}
if config.Except[fi.Name()] {
return nil
}
todo := []fixup{removeempty, checkname}
log.Printf("process %s", name)
err = one(name, todo...)
if err != nil {
log.Printf("%s: %s\n", name, err)
return err
}
return nil
})
if err != nil {
log.Fatal("%v", err)
}
}
}

View File

@ -0,0 +1,39 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
)
func main() {
flag.Parse()
a := flag.Args()
if len(a) != 2 {
fmt.Fprintf(os.Stderr, "[%v]usage: data2s name input-file (writes to stdout)\n", a)
os.Exit(1)
}
n := a[0]
i := a[1]
in, err := ioutil.ReadFile(i)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
total := len(in)
fmt.Printf("unsigned char %vcode[] = {\n", n)
for len(in) > 0 {
for j := 0; j < 16 && len(in) > 0; j++ {
fmt.Printf("0x%02x, ", in[0])
in = in[1:]
}
fmt.Printf("\n")
}
fmt.Printf("0,\n};\nint %vlen = %v;\n", n, total)
}

View File

@ -0,0 +1,113 @@
package main
import (
"bufio"
"debug/elf"
"flag"
"fmt"
"io"
"math"
"os"
"path"
)
var dry = flag.Bool("dryrun", true, "don't really do it")
func gencode(w io.Writer, n, t string, m []byte, start, end uint64) {
fmt.Fprintf(os.Stderr, "Write %v %v start %v end %v\n", n, t, start, end)
fmt.Fprintf(w, "int %v_%v_start = %v;\n", n, t, start)
fmt.Fprintf(w, "int %v_%v_end = %v;\n", n, t, end)
fmt.Fprintf(w, "int %v_%v_len = %v;\n", n, t, end-start)
fmt.Fprintf(w, "uint8_t %v_%v_out[] = {\n", n, t)
for i := uint64(start); i < end; i += 16 {
for j := uint64(0); i+j < end && j < 16; j++ {
fmt.Fprintf(w, "0x%02x, ", m[j+i])
}
fmt.Fprintf(w, "\n")
}
fmt.Fprintf(w, "};\n")
}
func main() {
flag.Parse()
a := flag.Args()
for _, n := range a {
f, err := elf.Open(n)
if err != nil {
fmt.Printf("%v %v\n", n, err)
continue
}
var dataend, codeend, end uint64
var datastart, codestart, start uint64
datastart, codestart, start = math.MaxUint64, math.MaxUint64, math.MaxUint64
mem := []byte{}
for _, v := range f.Progs {
if v.Type != elf.PT_LOAD {
continue
}
fmt.Fprintf(os.Stderr, "processing %v\n", v)
// MUST alignt to 2M page boundary.
// then MUST allocate a []byte that
// is the right size. And MUST
// see if by some off chance it
// joins to a pre-existing segment.
// It's easier than it seems. We produce ONE text
// array and ONE data array. So it's a matter of creating
// a virtual memory space with an assumed starting point of
// 0x200000, and filling it. We just grow that as needed.
curstart := v.Vaddr & ^uint64(0xfff) // 0x1fffff)
curend := v.Vaddr + v.Memsz
fmt.Fprintf(os.Stderr, "s %x e %x\n", curstart, curend)
if curend > end {
nmem := make([]byte, curend)
copy(nmem, mem)
mem = nmem
}
if curstart < start {
start = curstart
}
if v.Flags&elf.PF_X == elf.PF_X {
if curstart < codestart {
codestart = curstart
}
if curend > codeend {
codeend = curend
}
fmt.Fprintf(os.Stderr, "code s %v e %v\n", codestart, codeend)
} else {
if curstart < datastart {
datastart = curstart
}
if curend > dataend {
dataend = curend
}
fmt.Fprintf(os.Stderr, "data s %v e %v\n", datastart, dataend)
}
for i := uint64(0); i < v.Filesz; i++ {
if amt, err := v.ReadAt(mem[v.Vaddr+i:], int64(i)); err != nil && err != io.EOF {
fmt.Fprintf(os.Stderr, "%v: %v\n", amt, err)
os.Exit(1)
} else if amt == 0 {
if i < v.Filesz {
fmt.Fprintf(os.Stderr, "%v: Short read: %v of %v\n", v, i, v.Filesz)
os.Exit(1)
}
break
} else {
i = i + uint64(amt)
fmt.Fprintf(os.Stderr, "i now %v\n", i)
}
}
fmt.Fprintf(os.Stderr, "Processed %v\n", v)
}
fmt.Fprintf(os.Stderr, "gencode\n")
// Gen code to stdout. For each file, create an array, a start, and an end variable.
w := bufio.NewWriter(os.Stdout)
_, file := path.Split(n)
gencode(w, file, "code", mem, codestart, codeend)
gencode(w, file, "data", mem, datastart, dataend)
w.Flush()
}
}

View File

@ -0,0 +1,53 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Printf("usage: jsonpretty input.json [output.json]\n")
os.Exit(1)
}
buf, err := ioutil.ReadFile(os.Args[1])
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
var jsmap map[string]interface{}
if err := json.Unmarshal(buf, &jsmap); err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
for key, val := range jsmap {
switch tval := val.(type) {
case map[string]interface{}:
if len(tval) == 0 {
delete(jsmap, key)
}
case []interface{}:
if len(tval) == 0 {
delete(jsmap, key)
}
}
}
buf, err = json.MarshalIndent(jsmap, "", "\t")
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
buf = append(buf, '\n')
if len(os.Args) == 3 {
ioutil.WriteFile(os.Args[2], buf, 0666)
} else {
os.Stdout.Write(buf)
}
}

View File

@ -0,0 +1,115 @@
package main
import (
"debug/elf"
"encoding/binary"
"flag"
"fmt"
"io"
"math"
"os"
)
const LRES = 3