GAL SCHLEZINGER

developer by day; rocker by night

Lately, I’ve been working on a Rust clone of fnm. I like Rust and it is a great way to learn the language. Thanks to the Rust cross-platform tooling, I got a simple proof-of-concept for Windows support quickly.

The first thing I got to do was a install_node_dist method, which takes a borrowed Version struct and a path to install into. I wrote a simple test to install Node 12.0.0 and it seemed to work on Linux, MacOS and even Windows! I was excited, but since I don’t really use Windows and fnm works perfectly well for me, I dropped it (no pun intended.)


Not so long ago I saw some tweets by @zkat__ regarding a new package manager for Node written in Rust. It made me want to play with Rust again and I decided to copy the entire fnm end-to-end test suite into the Rust project. The E2E test suite was written in Bash (with some exception for Zsh and Fish), but I migrated it to a Rust DSL so I will be able to test the same code on Bash, Zsh and Fish — “write once, run everywhere”. Maybe I’ll write more about it in later posts.

When I started migrating the tests to PowerShell, I saw that there are some versions I fail to install, because of a “path too long” error. It was weird. But since I haven’t used Windows in years, I decided to install a dev Windows VM in order to make it work. After some debugging, it seemed that the deep directory structure of the Node modules that come with Node v8.11.3 were too deep for Windows to handle with its default configurations, and I had to make some changes.

I didn’t really know how to fix it, but while traversing the web (and finding this tweet by @zkat__) I saw a similar solution in a couple of repos, including Orogene — and applying it solved my issue.

Solving the Long Paths Issue

A Windows executable can have an embedded manifest that tells Windows how to handle the executable!

This is what I was missing. By embedding the following manifest to our binary we are able to use long paths freely. Let’s say this is fnm.manifest:

<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
        true
      </longPathAware>
    </windowsSettings>
  </application>
</assembly>

We now need to create a resource file (I don’t really know what it is) with the following contents:

#define RT_MANIFEST 24
1 RT_MANIFEST "fnm.manifest"

The last piece of the puzzle is to embed it into our Rust program. To do that, we need to install the following crate: embed-resource and create a Cargo build script:

fn main() {
    embed_resource::compile("fnm-manifest.rc");
}

This should hopefully make your path issues disappear. But maybe in the future it will be embedded into every Rust program, who knows?


Again, thanks again for the code I copied from Orogene. Speedy CLIs for Node development are making me excited: there’s no reason we have slow tools!

Many thanks to @zkat__ (for solving this in Orogene) & Michael Kutz.