{"id":183,"date":"2016-08-17T12:15:52","date_gmt":"2016-08-17T12:15:52","guid":{"rendered":"http:\/\/dlang.org\/blog\/?p=183"},"modified":"2021-10-08T11:10:29","modified_gmt":"2021-10-08T11:10:29","slug":"inside-d-version-manager","status":"publish","type":"post","link":"https:\/\/dlang.org\/blog\/2016\/08\/17\/inside-d-version-manager\/","title":{"rendered":"Inside D Version Manager"},"content":{"rendered":"<p><em>In his day job, Jacob Carlborg\u00a0is a Ruby backend developer for\u00a0<a href=\"http:\/\/www.derivco.se\">Derivco\u00a0Sweden<\/a>, but he&#8217;s been using D on his own time since 2006. He is the maintainer of numerous open source projects, including <a href=\"https:\/\/github.com\/jacob-carlborg\/dstep\">DStep<\/a>, a utility that generates D bindings from\u00a0C and Objective-C headers, <a href=\"https:\/\/github.com\/d-widget-toolkit\">DWT<\/a>, a port of the Java GUI library <a href=\"https:\/\/www.eclipse.org\/swt\/\">SWT<\/a>, and the topic of this post, <a href=\"https:\/\/github.com\/jacob-carlborg\/dvm\">DVM<\/a>. He implemented native Thread Local Storage support for DMD on OS X and contributed, along with Michel Fortin, to <a href=\"https:\/\/dlang.org\/spec\/objc_interface.html\">the integration of Objective-C<\/a>\u00a0in D.<\/em><\/p>\n<hr \/>\n<p><a href=\"https:\/\/github.com\/jacob-carlborg\/dvm\">D Version Manager (DVM)<\/a>, is a cross-platform tool that allows you to easily download, install and manage multiple D compiler versions. With DVM, you can select a specific version of the compiler to use without having to manually modify the <strong>PATH<\/strong> environment variable. A selected compiler is unique in each shell session, and it&#8217;s possible to configure a default compiler.<\/p>\n<p>The main advantage of DVM is the easy downloading and installation of different compiler versions. Specify the version of the compiler you would like to install, e.g. <code>dvm install 2.071.1<\/code>, and it will automatically download and install that version. Then you can tell DVM to use that version by executing <code>dvm use 2.071.1<\/code>. After that, you can invoke the compiler as usual with <code>dmd<\/code>. The selected compiler version will persist until the end of the shell session.<\/p>\n<p>DVM makes it possible for the user to select a specific compiler version without having to modify any makefiles or build scripts. It&#8217;s enough for any build script to refer to the compiler by name, i.e. <code>dmd<\/code>, as long as the user selects the compiler version with DVM before invoking the script.<\/p>\n<h2>History<\/h2>\n<p>DVM was created in the beginning of 2011. That was a different time for D. No proper installers existed, D1 was still a viable option, and each new release of DMD brought with it a number of regressions.\u00a0Because of all the regressions, it was basically impossible to always use the latest compiler, and often even older compilers, for all of your projects. Taking into consideration projects from other developers, some were written in D1 and some in D2, making it inconvenient to have only one compiler version installed.<\/p>\n<p>It was for these reasons I created DVM. Being able to have different versions of the compiler active in different shell sessions makes it easy to work on different projects requiring different versions of the compiler. For example, it was possible to open one tab for a D1 compiler and another for a D2 compiler.<\/p>\n<p>The concept of DVM comes directly from the Ruby tool <a href=\"https:\/\/rvm.io\">RVM<\/a>. Where DVM installs D compilers, RVM installs Ruby interpreters. RVM can do everything DVM can do and a lot more. One of the major things I did <em>not<\/em> want to copy from RVM is that it&#8217;s completely written in shell script (bash). I wanted DVM to be written in D. Because it&#8217;s written in shell script, RVM enables some really useful features that DVM does not support, but some of them are questionable (some might call them hacks). For example, when navigating to an RVM-enabled project, RVM will automatically select the correct Ruby interpreter. However, it accomplishes this by overriding the built-in <code>cd<\/code>\u00a0command. When the command is invoked, RVM will look in the target directory for one of the files .<strong>rvmrc<\/strong> or <strong>.ruby-version<\/strong>. If either is present, it will read that file to determine\u00a0which Ruby interpreter to select.<\/p>\n<h2>Implementation and Usage<\/h2>\n<p>One of the goals of DVM was that it should be implemented in D. In the end, it was mostly written in D with a few bits of shell script. Note that the following implementation details are specific to the platforms that fall under D&#8217;s <strong>Posix<\/strong> umbrella, i.e. <code>version(Posix)<\/code>, but DVM is certainly available for Windows with the same functionality.<\/p>\n<h3>Structure of the DVM Installation<\/h3>\n<p>Before DVM can be used, it needs to install itself. This is accomplished with the command, <code>dvm install dvm<\/code>. This will create the <strong>~\/.dvm<\/strong> directory. It contains the following subdirectories: <strong>archives<\/strong>, <strong>bin<\/strong>, <strong>compilers<\/strong>, <strong>env<\/strong> and <strong>scripts<\/strong>.<\/p>\n<p><strong>archives<\/strong> contains a cache of downloaded zip archives of D compilers.<\/p>\n<p><strong>bin<\/strong> contains shell scripts, acting as symbolic links, to all installed D compilers. The name of each contains the version of the compiler, e.g. <strong>dmd-2.071.1<\/strong>, making it possible to invoke a specific compiler without first having to invoke the <code>use<\/code> command. This directory also contains one shell script, <strong>dvm-current-dc<\/strong>, pointing to the currently active D compiler. This allows the currently active D compiler to be invoked without knowing which version has been set. This can be useful for executing the compiler from within an editor or IDE, for example. A shell script for the default compiler exists as well. Finally, this directory also contains the binary <strong>dvm<\/strong> itself.<\/p>\n<p>The <strong>compilers<\/strong> directory contains all installed compilers. All of the downloaded compilers are unpacked here. Due to the varying quality of the D compiler archives throughout the years, the <code>install<\/code> command will also make a few adjustments if necessary. In the old days, there was only one archive for all platforms. This command will only include binaries and libraries for the current platform. Another adjustment is to make sure all executables have the executable permission set.<\/p>\n<p>The <strong>env<\/strong> directory contains helper shell scripts for the <code>use<\/code> command. There&#8217;s one script for each installed compiler and one for the default selected compiler.<\/p>\n<p>The <strong>scripts<\/strong> directory currently only contains one file, <strong>dvm<\/strong>. It&#8217;s a shell script which wraps the <strong>dvm<\/strong> binary in the <strong>bin<\/strong> directory. The purpose of this wrapper is to aid the <code>use<\/code>\u00a0command.<\/p>\n<h3>The use Command<\/h3>\n<p>The most interesting part of the implementation is the <code>use<\/code> command, which selects a specific compiler, e.g. <code>dvm use 2.071.1<\/code>. The selection of a compiler will persist for the duration of the shell session (window, tab, script file).<\/p>\n<p>The command works by prepending the path of the specified compiler to the <strong>PATH<\/strong> environment variable. This can be <strong>~\/.dvm\/compilers\/dmd-2.071.1\/{platform}\/bin<\/strong> for example, where <strong>{platform}<\/strong> is the currently running platform. By\u00a0prepending the path to the environment variable, it guarantees\u00a0the selected compiler takes precedence over any other possible compilers in the <strong>PATH<\/strong>. The reason the <strong>{platform}<\/strong> section of the path exists is related to the structure of the downloaded archive. Keeping this structure avoids having to modify the compiler&#8217;s configuration file, <strong>dmd.conf<\/strong>.<\/p>\n<p>The interesting part here is that it&#8217;s not possible to modify the environment variables of the parent process, which in this case is the shell. The magic behind the <code>use<\/code> command is that the <code>dvm<\/code> command that you&#8217;re actually invoking is not the D binary; it&#8217;s the shell script in the <strong>~\/.dvm\/scripts<\/strong> path. This shell script contains a function called <code>dvm<\/code>. This can be verified by invoking <code>type dvm | head -n 1<\/code>, which should print <em>dvm is a function<\/em> if everything is installed correctly.<\/p>\n<p>The installation of DVM adds a line to the shell initialization file, <strong>.bashrc<\/strong>, <strong>.bash_profile<\/strong> or similar. This line will load\/source the DVM shell script in the <strong>~\/.dvm\/scripts<\/strong> path which will make the <code>dvm<\/code> command available. When the <code>dvm<\/code> function is invoked, it will forward the call to the <strong>dvm<\/strong> binary located in <strong>~\/.dvm\/bin\/dvm<\/strong>. The <strong>dvm<\/strong> binary contains all of the command logic. When the <code>use<\/code> command is invoked, the <strong>dvm<\/strong> binary will write a new file to <strong>~\/.dvm\/tmp\/result<\/strong> and exit. This file contains a command for loading\/sourcing the environment file available in <strong>~\/.dvm\/env<\/strong> that corresponds to the version that was specified when the <code>use<\/code> command was invoked. After the <strong>dvm<\/strong> binary has exited, the shell script function takes over again and loads\/sources the result file if it exists. Since the shell script is loaded\/sourced instead of executed, the code will be evaluated in the current shell instead of a sub-shell. This is what makes it possible to modify the <strong>PATH<\/strong> environment variable. After the result file is loaded\/sourced, it&#8217;s removed.<\/p>\n<p>If you find yourself with the need to build your D project(s) with multiple compiler versions, such as the current release of DMD, one or more previous releases, and\/or the latest beta, then DVM will allow you to do so in a hassle-free manner. Pull up a shell, execute <code>use<\/code>\u00a0on the version you want, and away you go.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In his day job, Jacob Carlborg\u00a0is a Ruby backend developer for\u00a0Derivco\u00a0Sweden, but he&#8217;s been using D on his own time since 2006. He is the maintainer of numerous open source projects, including DStep, a utility that generates D bindings from\u00a0C and Objective-C headers, DWT, a port of the Java GUI library SWT, and the topic [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[12,9],"tags":[],"_links":{"self":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/183"}],"collection":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/comments?post=183"}],"version-history":[{"count":12,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/183\/revisions"}],"predecessor-version":[{"id":308,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/posts\/183\/revisions\/308"}],"wp:attachment":[{"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/media?parent=183"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/categories?post=183"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dlang.org\/blog\/wp-json\/wp\/v2\/tags?post=183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}