jiri

“Jiri integrates repositories intelligently”
is the official description as well as a recursive acronym for jiri.

It is a multi-repo management tool used internally at Google for their Fuchsia OS development. The project manifest format is pretty much like repo, which is commonly used for Android development and was later adopted by other communities like CoreOS.

I came across Fuchsia even before its kernel was renamed to Zircon, and at that time, the Fuchsia OS itself is pretty much unusable. But I found jiri particularly useful for my own projects. Since then, I started using jiri for my own large projects that requires splitting repositories and gather them together to build (like my own libraries, my build tools, etc.)

Motivation

The workflow using jiri has been fine until recently. I send my graphics card back to the manufacturor for repairing, and therefore I have to shutdown my desktop until I received the repaired GPU.
In my desktop machine previously, I have various OSes all configured ready for development:

  • Manjaro Linux latest
  • Hackintosh 10.12.6 high sierra
  • Windows 10 (insider)
  • Solus (for entertainment)

And jiri works pretty well on my Linux development environment as well as that illegal Hackintosh ;)

But now I’m stuck with a laptop running only Windows 10 Home …

I’ve already setup all the required dev tools (I assume you did if you want to build jiri on Windows as well), but in jiri’s documentation it is said to only compile under Linux and macOS, so I patched it to make it at least build on Windows (since most of it is written in platform independent Go code).

The Patch

diff --git a/cmd/jiri/cmd.go b/cmd/jiri/cmd.go
index d6761be..82e801b 100644
--- a/cmd/jiri/cmd.go
+++ b/cmd/jiri/cmd.go
@@ -8,30 +8,13 @@
 package main
 
 import (
-    "fmt"
     "runtime"
-    "syscall"
-
     "fuchsia.googlesource.com/jiri/cmdline"
 )
 
 func init() {
     runtime.GOMAXPROCS(runtime.NumCPU())
-    if runtime.GOOS == "darwin" {
-        var rLimit syscall.Rlimit
-        err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
-        if err != nil {
-            fmt.Println("Unable to obtain rlimit: ", err)
-        }
-        if rLimit.Cur < rLimit.Max {
-            rLimit.Max = 999999
-            rLimit.Cur = 999999
-            err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
-            if err != nil {
-                fmt.Println("Unable to increase rlimit: ", err)
-            }
-        }
-    }
+    // darwin rlimit manipulation moved to conditional compilation
     cmdRoot = newCmdRoot()
 }
 
diff --git a/textutil/util.go b/textutil/util.go
index 7f03047..63a8cdf 100644
--- a/textutil/util.go
+++ b/textutil/util.go
@@ -5,40 +5,28 @@
 package textutil
 
 import (
-    "syscall"
-    "unsafe"
+    "golang.org/x/crypto/ssh/terminal"
+    "os"
 )
 
 // TerminalSize returns the dimensions of the terminal, if it's available from
 // the OS, otherwise returns an error.
 func TerminalSize() (row, col int, _ error) {
-    // Try getting the terminal size from stdout, stderr and stdin respectively.
-    // We try each of these in turn because the mechanism we're using fails if any
-    // of the fds is redirected on the command line.  E.g. "tool | less" redirects
-    // the stdout of tool to the stdin of less, and will mean tool cannot retrieve
-    // the terminal size from stdout.
-    //
-    // TODO(toddw): This probably only works on some linux / unix variants; add
-    // build tags and support different platforms.
-    if row, col, err := terminalSize(syscall.Stdout); err == nil {
+    // Try getting the terminal size from stdout, stderr and stdin respectively
+    // using terminal.GetSize function, which should work on most platforms.
+
+    fdOut := int(os.Stdout.Fd())
+    fdIn := int(os.Stdin.Fd())
+    fdErr := int(os.Stderr.Fd())
+
+    if row, col, err := terminal.GetSize(fdOut); err == nil {
         return row, col, err
     }
-    if row, col, err := terminalSize(syscall.Stderr); err == nil {
+
+    if row, col, err := terminal.GetSize(fdErr); err == nil {
         return row, col, err
     }
-    return terminalSize(syscall.Stdin)
-}
 
-func terminalSize(fd int) (int, int, error) {
-    var ws winsize
-    if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&ws))); err != 0 {
-        return 0, 0, err
-    }
-    return int(ws.row), int(ws.col), nil
+    return terminal.GetSize(fdIn)
 }
 
-// winsize must correspond to the struct defined in "sys/ioctl.h".  Do not
-// export this struct; it's a platform-specific implementation detail.
-type winsize struct {
-    row, col, xpixel, ypixel uint16
-}

Basically, I replaced the “get terminal size” function with the golang.org/x/crypto/ssh/terminal’s GetSize() functions and moved darwin-specific initializing code to a separate file that is only processed when GOOS is darwin.

Building git2go

This is the painful part, since git2go did not provide any documentation on how to build on Windows, and most people do that in msys/cygwin, which I don’t feel comfortable with. The compilation process I tried uses gcc, go nothing else on Windows.

Package Manager: Scoop

Scoop is a tool I discovered recently, before that I’d always used Chocolatey. The advantage of Scoop over Chocolatey is that it updates packages much more frequently and almost all of its packages are up to date.

Installing packages is not hard as well:

scoop install -g go gcc cmake pkg-config

This would get most of the dependencies required for building jiri installed.

Build libgit2

git clone https://github.com/libgit2/libgit2.git
cd libgit2
# git checkout whatever
mkdir build; cd build
cmake .. -DCMAKE_C_COMPILER={path to your}/gcc.exe -DCMAKE_CXX_COMPILER={path to your}/g++.exe -DCMAKE_INSTALL_PREFIX={whatever}
mingw32-make -j16
mingw32-make install

Set the pkg-config path

set PKG_CONFIG_PATH={whatever prefix}/lib/pkgconfig;

Building jiri

Apply the patch and Build

patch -Np1 {path to your}/jiri-win.patch
go build fuchsia.googlesource.com/jiri/cmd/jiri

Now you have jiri.exe inside the project root

Copy the libgit2.dll

jiri.exe requires libgit2.dll available if you built it dynamically, therefore you need to copy that dll either to the same directory as jiri.exe or somewhere Windows could find it…

There we Go

jiri windows