Notes

Home About Contact Archive
GitHub LinkedIn Twitter CoBUG.org

Measuring the weight of an electron

Electrons are small, should be easy. Right?!
Posted on Thu, 01 Jun 2017 08:18:00 MDT by Aaron Bieber
Tags: OpenBSD, Electron, Software

I am going to “Measure the weight of an electron”! By “weight”, I mean what it takes to make Electron work on OpenBSD.

This is a long rant. A rant intended to document lunacy, hopefully aid others in the future and make myself feel better about something I think is crazy. It may seem like I am making an enemy of electron, but keep in mind that isn’t my intention! The enemy here, is complexity!

My friend Henry, a canary, is coming along for the ride!

Getting the tools

At first glance Electron seems like a pretty solid app, it has decent docs, it’s consolidated in a single repository, has a lot of visibility, porting it shouldn’t be a big deal, right?

First things first, clone that repo!

git clone git@github.com:electron/electron.git

If you want to follow along, we will be using the build instructions for linux doc.

Reading through the doc, right off the bat there are a few interesting things:

  • At least 25GB disk space

Huh, OK, some how this ~47M repository is going to blow up to 25G? I glance at Henry, he gives me the “what?” look. We carry on.

  • Clang 3.4 or later.

This one isn’t odd until we have more context. More on this one later.

Continuing along with the build, I know I have two versions of clang installed on OpenBSD, one from ports and one in base. Hopefully I will be able to tell the build to use one of these versions.

Indeed Electron has that ability! Their example is even using the same prefix OpenBSD’s clang port!

So, we run the bootstrap:

./script/bootstrap.py -v --clang_dir /usr/local
Traceback (most recent call last):
  File "./script/bootstrap.py", line 10, in <module>
    from lib.config import BASE_URL, PLATFORM,  enable_verbose_mode, \
  File "/home/qbit/dev/electron_wut/script/lib/config.py", line 17, in <module>
    }[sys.platform]
KeyError: 'openbsd6'

Dang. Looks like we need to tell bootstrap about OpenBSD. Easy enough:

diff --git a/script/lib/config.py b/script/lib/config.py
index 58f467b5b..646af08f7 100644
--- a/script/lib/config.py
+++ b/script/lib/config.py
@@ -14,6 +14,7 @@ PLATFORM = {
   'darwin': 'darwin',
   'linux2': 'linux',
   'win32': 'win32',
+  'openbsd6': 'openbsd',
 }[sys.platform]

 verbose_mode = False

We re-run the bootstrap, things seem to be going well.. Then the Henry squeaks: “whoa!!”:

Synchronizing submodule url for 'vendor/requests'
git submodule update --init --recursive
Cloning into '/home/qbit/dev/electron_wut/vendor/boto'...
error: object c1eddff4ee3f62b6039f1083651b9118883e7f07: badTimezone: invalid author/committer line - bad time zone
fatal: Error in object
fatal: index-pack failed
fatal: clone of 'https://github.com/boto/boto.git' into submodule path '/home/qbit/dev/electron_wut/vendor/boto' failed
Failed to clone 'vendor/boto'. Retry scheduled
Cloning into '/home/qbit/dev/electron_wut/vendor/breakpad'...

We just failed to clone the boto repo, but the build is still going.. does this mean it was an optional dependency and isn’t needed for the build?

Henry doesn’t look happy, none the less, he assures me it’s OK to go on. What a trooper!

Cloning into '/home/qbit/dev/electron_wut/vendor/requests'...
error: object 5e6ecdad9f69b1ff789a17733b8edc6fd7091bd8: badTimezone: invalid author/committer line - bad time zone
fatal: Error in object
fatal: index-pack failed
fatal: clone of 'https://github.com/kennethreitz/requests' into submodule path '/home/qbit/dev/electron_wut/vendor/requests' failed
Failed to clone 'vendor/requests'. Retry scheduled
Cloning into '/home/qbit/dev/electron_wut/vendor/boto'...
error: object c1eddff4ee3f62b6039f1083651b9118883e7f07: badTimezone: invalid author/committer line - bad time zone
fatal: Error in object
fatal: index-pack failed
fatal: clone of 'https://github.com/boto/boto.git' into submodule path '/home/qbit/dev/electron_wut/vendor/boto' failed
Failed to clone 'vendor/boto' a second time, aborting

Wait. Another repository failed to clone? At least this time the build failed after trying to clone boto.. again. I am guessing it tried twice because something might have changed between now and the last clone? Off in the distance we catch a familiar tune, it almost sounds like Gnarls Barkley’s song Crazy, can’t tell for sure.

As it turns out, if you are using git-fsck, you are unable to clone boto and requests. Obviously the proper fix for his is to not care about the validity of the git objects!

So we die a little inside and comment out fsckobjects in our ~/.gitconfig.

I look at Henry, he assures me it’s safe to go on.

We re-run bootstrap… thousands of lines fly past.. “npm verb... something something”. I can only think npm is puking this info for it’s on benefit. It definitely isn’t for ours!

Bah, another error:

subprocess.CalledProcessError: Command '['/usr/local/bin/python', '/home/qbit/dev/electron_wut/vendor/libchromiumcontent/script/download', '-s', '-f', '-c', '94c58176db175d72d88621afe8223b4175eecba5', '--target_arch', 'x64', 'https://s3.amazonaws.com/github-janky-artifacts/libchromiumcontent', '/home/qbit/dev/electron_wut/vendor/download/libchromiumcontent']' returned non-zero exit status 1

Looks like it’s trying to download a pre-built libchromiumcontent, we reference the doc again, finding the --build_libchromiumcontent option. Re-run!

This time we are faced with .’s, I have no idea what is happening:

/usr/local/bin/python /home/qbit/dev/electron_wut/vendor/libchromiumcontent/script/update -t x64 --defines  make_clang_dir=/usr/local clang_use_chrome_plugins=0
.......

Looking in top, we can see a python process with a WAIT of netio, maybe it’s downloading something? Looking in the electron_wut directory reveals a growing file named chromium-58.0.3029.110.tar.xz. We let it finish downloading.

Out of curiosity we look at vendor/libchromiumcontent/script/update, it seems its purpose is to download / extract chromium clang and node, good thing we already specified --clang_dir or it might try to build clang again!

544 dots and 45 minutes later, we have an error! The chromium-58.0.3029.110.tar.xz file is mysteriously not there anymore.. Interesting.

Scrolling up in the terminal points us to something disheartening:

Extracting...
Updating Clang to 296320-1...
Creating directory /home/qbit/dev/electron_wut/vendor/libchromiumcontent/src/third_party/llvm-build
Traceback (most recent call last):
File "/home/qbit/dev/electron_wut/vendor/libchromiumcontent/src/tools/clang/scripts/update.py", line 902, in <module>
    sys.exit(main())
    File "/home/qbit/dev/electron_wut/vendor/libchromiumcontent/src/tools/clang/scripts/update.py", line 898, in main return UpdateClang(args)
    File "/home/qbit/dev/electron_wut/vendor/libchromiumcontent/src/tools/clang/scripts/update.py", line 420, in UpdateClangi assert sys.platform.startswith('linux')
AssertionError
None

Wut. “Updating Clang…”. Didn’t I explicitly say not to build clang?

At this point we have to shift projects, no longer are we working on Electron.. It’s libchromiumcontent that needs our attention.

Fixing sub-tools

git clone git@github.com:electron/libchromiumcontent.git

Following the instructions on this repo, we run script/bootstrap.. it seems to complete without issue!

On to the next steps:

script/update -t x64

Ahh, our old friends the dots! This is the second time waiting 45+ minutes for a 500+ MB file to download. We are fairly confident it will fail, delete the file out from under itself and hinder the process even further, so we add an explicit exit to the update script. This way we can copy the file somewhere safe!

diff --git a/script/update b/script/update
index 234e4b3..b2639bf 100755
--- a/script/update
+++ b/script/update
@@ -107,6 +107,7 @@ def download_source_tarball(version):
         sys.stderr.flush()
         t.write(chunk)

+  sys.exit()
   sys.stderr.write('\nExtracting...\n')
   sys.stderr.flush()

544 dots and 43 minutes later…. chromium-58.0.3029.110.tar.xz is safe!

Fool me once…

mkdir safe_space
cp chromium-58.0.3029.110.tar.xz safe_space/

We remove the sys.exit() and re-run! Wut.. dots again!? Lets look deeper into this update script:

if not args.no_download:
  version = chromium_version()
  if not is_source_tarball_updated(version):
    download_source_tarball(version)
else:
  print "Skipping Chromium Source Tarball Download"

Ok, lets try that.. We copy the tar.xz out of its safe_space…

Skipping Chromium Source Tarball Download
Traceback (most recent call last):
  File "/home/qbit/dev/libchromiumcontent/vendor/python-patch/patch.py", line 1136, in <module>
    patch.apply(options.strip, root=options.directory) or sys.exit(-1)
  File "/home/qbit/dev/libchromiumcontent/vendor/python-patch/patch.py", line 778, in apply
    os.chdir(root)
OSError: [Errno 2] No such file or directory: '/home/qbit/dev/libchromiumcontent/src/.'

Sigh. The above (or similar) was repeated about 50 times… The trend here seems to be: Ignore errors! They are stupid and meaningless anyway!

Since def download_source_tarball should actually be def download_source_tarball_then_extract, we do that part for it… and pat ourselves on the back for having a safe_space!

As chromium extracts, Henry and I can’t shake the feeling that everything until now was just the tip of the iceberg

We remove the call to update_clang, because.. well.. we have two copies of it already and the Electron doc said everything would be fine if we had >= clang 3.4!

Re-run..

already patched  third_party/WebKit/Source/core/paint/ThemePainterMac.mm
already patched  third_party/WebKit/Source/platform/mac/KillRingMac.mm
qbit@slip[1]:libchromiumcontent[master *%=]λ

That [1] in my PS1 means that the update script exited with the return code 1… but there is no indication of why..

Henry’s lovely yellow plumage seems to be becoming a darker shade of yellow.. How much more of this can we take?!

If we have learned anything so far, it has to be “errors don’t matter!”. This one, however, warrants further investigation!

We dig deeper into script/update

update_gn().. pulls down a binary gn.. which, interestingly, can be generated with the code we have right below our feet… but for some reason, they have this component already built. There is no pre-built version for OpenBSD.

At this point, Henry and I are getting pretty irritated.. it’s time to bring in some big guns! We are going to leverage the countless hours of work that have already been put into properly building these components! (novel, right?!)

We quickly move to /usr/ports/www/chromium, low and behold, it’s the exact version that libchromiumcontent is trying to build! We review the Makefile to find this gem: 2. bootstrap gn, the tool to generate ninja files

Running make configure quickly gets us a usable gn binary, we make the appropriate directories under src/buildtools, copy gn in, modify our script/update file:

diff --git a/script/update b/script/update
index 234e4b3..b5c4afc 100755
--- a/script/update
+++ b/script/update
@@ -52,9 +52,9 @@ def main():

   return (apply_patches() or
           copy_chromiumcontent_files() or
-          update_clang() or
-          update_gn() or
-          update_node() or
+          #update_clang() or
+          #update_gn() or
+          #update_node() or
           run_gn(target_arch, args.defines))


@@ -248,6 +248,8 @@ def run_gn(target_arch, defines):
     gn = os.path.join(SRC_DIR, 'buildtools', 'linux64', 'gn')
   elif sys.platform == 'darwin':
     gn = os.path.join(SRC_DIR, 'buildtools', 'mac', 'gn')
+  elif sys.platform == 'openbsd6':
+    gn = os.path.join(SRC_DIR, 'buildtools', 'openbsd', 'gn')

   env = os.environ.copy()
   if sys.platform in ['win32', 'cygwin']:

Re-run!

ERROR at //build/config/sysroot.gni:95:5: Assertion failed.
    assert(
    ^-----
Missing sysroot (//build/linux/debian_wheezy_amd64-sysroot). To fix, run: build/linux/sysroot_scripts/install-sysroot.py --arch=amd64
See //build/config/sysroot.gni:96:9:
        exec_script("//build/dir_exists.py",
        ^-----------------------------------

Wheezy?! Where is that getting set?! We stop and ponder.. how the hell did we get here? What could have possibly warranted abandoning makefiles and shell scripts in favor of this monstrosity!?

Just for fun.. lets try to run the second step (after all, the first step only produced a 1, right!?)

script/build -t x64

FML:

qbit@slip[1]:libchromiumcontent[master *%=]λ script/build -t x64
Unsupported OS OpenBSD
No prebuilt ninja binary was found for this system.
Try building your own binary by doing:
  cd ~
  git clone https://github.com/martine/ninja.git -b v1.7.2
  cd ninja && ./configure.py --bootstrap
Then add ~/ninja/ to your PATH.
Traceback (most recent call last):
  File "script/build", line 57, in <module>
    sys.exit(main())
  File "script/build", line 43, in main
    subprocess.check_call([NINJA, '-C', os.path.relpath(out_dir), target], env=env)
  File "/usr/local/lib/python2.7/subprocess.py", line 186, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/home/qbit/dev/libchromiumcontent/vendor/depot_tools/ninja', '-C', 'src/out-x64/static_library', 'chromiumcontent:chromiumcontent']' returned non-zero exit status 1
qbit@slip[1]:libchromiumcontent[master *%=]λ which ninja
/usr/local/bin/ninja
qbit@slip[0]:libchromiumcontent[master *%=]λ

Clearly we are dealing with a beast that is too smart for its own good.

Fixing sub-sub-tools

Since depot_tools is a Google project, it’s easier to edit the files in the vendor directory and pretend nothing ever happened.

diff --git a/ninja b/ninja
index 282cc276..e22cbb9a 100755
--- a/ninja
+++ b/ninja
@@ -37,7 +37,5 @@ case "$OS" in
   Darwin)    exec "${THIS_DIR}/ninja-mac" "$@";;
   CYGWIN*)   exec cmd.exe /c $(cygpath -t windows $0).exe "$@";;
   MINGW*)    cmd.exe //c $0.exe "$@";;
-  *)         echo "Unsupported OS ${OS}"
-             print_help
-             exit 1;;
+  *)         exec "/usr/local/bin/ninja" "$@";;
 esac

Sigh. So many assumptions, lets continue the trend!

cd ../../

Re-run script/build -t x64…

No luck. At this point we are faced with a complex web of python scripts that execute gn on GN files to produce ninja files… which then build the various components and somewhere in that cluster, something doesn’t know about OpenBSD…

I look at Henry, he is looking a photo of his wife and kids. They are sitting on a telephone wire, the morning sun illuminating their beautiful faces. Henry looks back at me and says “It’s not worth it.”

We slam the laptop shut and go outside.


Made by qbit. Subscribe via RSS / Atom | Generated using boring and these files.
PGP: 0x1F81112D62A9ADCE / 3586 3350 BFEA C101 DB1A 4AF0 1F81 112D 62A9 ADCE
proof.