<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jør∂¡</title>
    <description>The latest articles on DEV Community by Jør∂¡ (@brickpop).</description>
    <link>https://dev.to/brickpop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F295707%2F04c6a589-c7de-43cc-9ac1-a2efe5f81489.jpg</url>
      <title>DEV Community: Jør∂¡</title>
      <link>https://dev.to/brickpop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brickpop"/>
    <language>en</language>
    <item>
      <title>My dream come true: Launching GUI Docker sessions with DX11</title>
      <dc:creator>Jør∂¡</dc:creator>
      <pubDate>Tue, 07 Jan 2020 22:48:50 +0000</pubDate>
      <link>https://dev.to/brickpop/my-dream-come-true-launching-gui-docker-sessions-with-dx11-in-seconds-1a53</link>
      <guid>https://dev.to/brickpop/my-dream-come-true-launching-gui-docker-sessions-with-dx11-in-seconds-1a53</guid>
      <description>&lt;p&gt;Get ready, because today we will get you to &lt;strong&gt;launch complete Linux Desktop Environments&lt;/strong&gt; on demand. You’ll create your ideal graphical environment in a few minutes and launch it in a few seconds. Launch any desktop in the same way as you would launch a program.&lt;/p&gt;

&lt;p&gt;Sounds good, doesn’t it?&lt;/p&gt;

&lt;p&gt;If you are a Linux user, at some point you may have used Virtual Machines to try new stuff and experiment before you choose one. Virtualization is cool, but you know the downsides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need to boot a separate OS with its own Kernel and added overhead&lt;/li&gt;
&lt;li&gt;The guest OS preallocates a fixed amount of RAM memory, even if only 1% of it is used&lt;/li&gt;
&lt;li&gt;Your guest OS will never run as smoothly as your native desktop&lt;/li&gt;
&lt;li&gt;The guest OS also needs a virtual data partition, networking, guest tools, etc&lt;/li&gt;
&lt;li&gt;You need to manually install it and tweak it to your needs&lt;/li&gt;
&lt;li&gt;If an upgrade breaks your system, there is no clean way to go back. With snapshots you would lose the latest changes on your personal data, whereas uninstalling or downgrading is not guaranteed to work&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A new hope: Docker
&lt;/h2&gt;

&lt;p&gt;Something instantly “clicked” in my head when I first heard of Docker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run any Linux command from any OS image, reusing the same Kernel and system resources.&lt;/li&gt;
&lt;li&gt;Run on disposable stateless containers that you could break and relaunch right after.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  That’s so cool, why don’t we all live on Docker containers right now?
&lt;/h1&gt;
&lt;/blockquote&gt;

&lt;p&gt;Docker is mainly &lt;strong&gt;intended for backend projects&lt;/strong&gt;. It’s designed for the command line and targeted to run servers, daemons, compilers and so on. Docker empowers great pieces of software, but &lt;strong&gt;Desktop Environments are not one of them&lt;/strong&gt; yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter x11docker
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/mviereck/x11docker"&gt;X11Docker&lt;/a&gt; is a feature rich script that addresses this problem. It does the heavy lifting to start an X11 server on the host system and makes Dockerized apps talk to it. So with it, you can run GUI apps from the Docker image that you like! 😃🎉&lt;/p&gt;

&lt;p&gt;I’ve been using it for a month now. While I applaud the brilliant work that it has behind, my conclusion is that &lt;strong&gt;X11Docker is not a VM replacement&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead, I see it as a replacement for docker run  . &lt;br&gt;
A minimal VM-like scenario looks much more like:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Unless you make a script of it, I bet you’ll never use x11docker for it. And yet this does not even include home folder encryption, direct connectivity with the host, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  The missing piece: DX11
&lt;/h2&gt;

&lt;p&gt;My yearn for multiple independent Desktop systems launched on demand kept growing until I decided to ditch the script above and program a full fledged tool that made my dream come true.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G5T9qH0W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/10562/0%2ALQoXBm1o5HOfPfZx" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G5T9qH0W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/10562/0%2ALQoXBm1o5HOfPfZx" alt="Photo by [Adam Whitlock](https://unsplash.com/@adam_whitlock?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)"&gt;&lt;/a&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@adam_whitlock?utm_source=medium&amp;amp;utm_medium=referral"&gt;Adam Whitlock&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Without further ado, let me &lt;strong&gt;introduce DX11&lt;/strong&gt;! 😃🎉&lt;br&gt;
&lt;a href="https://gitlab.com/brickpop/dx11-nim#dx11"&gt;&lt;strong&gt;DX11&lt;/strong&gt; / &lt;em&gt;CLI manager for X11 Docker Linux sessions&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DX11 is a Linux program that &lt;strong&gt;allows you to run X11 Linux sessions with Docker&lt;/strong&gt;. A “session” consists of a Docker image, a user home folder and settings like user data encryption or Tor networking.&lt;/p&gt;

&lt;p&gt;DX11 combines the great performance of Docker on Linux with many of the features you’d get when running VM’s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As fluid and fast as your native system&lt;/li&gt;
&lt;li&gt;Reuse the same kernel, system memory and disk storage&lt;/li&gt;
&lt;li&gt;Scriptable, 100% reproducible desktop environments&lt;/li&gt;
&lt;li&gt;Immutable desktop systems (similar in concept to &lt;a href="https://silverblue.fedoraproject.org/"&gt;Silverblue&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Disposable and throwaway sessions&lt;/li&gt;
&lt;li&gt;Independent home folder persistence for each session&lt;/li&gt;
&lt;li&gt;Optional home folder encryption&lt;/li&gt;
&lt;li&gt;Optional Tor networking&lt;/li&gt;
&lt;li&gt;Unprivileged execution, with the option to attach separately as root&lt;/li&gt;
&lt;li&gt;Isolated sandboxed environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Up9HCds--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AFPid7eu9uY88T6kYZDsang.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Up9HCds--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AFPid7eu9uY88T6kYZDsang.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xGUkBvn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AQjKR-JZ9ME5yyUOlTjugYQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xGUkBvn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AQjKR-JZ9ME5yyUOlTjugYQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7APj3Mad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AnWDGNgCP8v4zszY1azLY_g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7APj3Mad--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AnWDGNgCP8v4zszY1azLY_g.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Use cases
&lt;/h3&gt;

&lt;p&gt;The list grows pretty much as you imagination allows. But a few examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Realistic distro hopping&lt;/strong&gt;. Try a new distribution or desktop environment before you make it your daily driver. Evaluate the real performance instead of hoping that a native installation would be smoother.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full portability&lt;/strong&gt;. Copy your session’s data to a brand new computer, rebuild the Docker image and continue to work as if nothing ever happened.&lt;/li&gt;
&lt;li&gt;Give your developers a &lt;strong&gt;reference workstation&lt;/strong&gt; Dockerfile that they can still customize. Avoid the “works on my machine” problem.&lt;/li&gt;
&lt;li&gt;Speed up the &lt;strong&gt;landing of new developers&lt;/strong&gt; with an already functional toolkit.&lt;/li&gt;
&lt;li&gt;Post your session’s Dockerfile on your &lt;strong&gt;StackOverflow question&lt;/strong&gt; or &lt;strong&gt;GitHub issue&lt;/strong&gt;. Let others reproduce the exact same environment you have when you encounter that nasty error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deprecation proof&lt;/strong&gt; development environments. Your legacy React Native or Cordova setup is carved in stone. If an SDK upgrade breaks a project, nothing else will be affected. Rerun your stable image and npm install will keep working as usual.&lt;/li&gt;
&lt;li&gt;Post your session’s Dockerfile on a public repo. Let others clone it, fork it and make it even cooler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evaluate bloated/unsafe software&lt;/strong&gt; on a clone of your workstation and drop the changes when you are done experimenting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade and downgrade&lt;/strong&gt; at will, without the risk of losing any local data. Docker build will never interact with any session, even if it’s running.&lt;/li&gt;
&lt;li&gt;Replace your all-in-one bloated computer with &lt;strong&gt;minimal isolated systems&lt;/strong&gt; that you can use on demand. One for your sysadmin work, one for your side projects, one for gaming, one for anonymous browsing, etc.&lt;/li&gt;
&lt;li&gt;Forget about &lt;strong&gt;multiple SSH key management&lt;/strong&gt; by having a different session for each project, with its corresponding key files and credentials.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cybercafes&lt;/strong&gt;, where a Linux computer spins up a fresh disposable desktop when you pay for it and terminates it when you are done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Record a screencast of that &lt;em&gt;upcoming&lt;/em&gt; Gnome/KDE release&lt;/strong&gt; and transfer the video file to your main system right away. No VirtualBox Guest Additions needed.&lt;/li&gt;
&lt;li&gt;If you &lt;strong&gt;write extensions&lt;/strong&gt; for KDE, Gnome, etc., you can publish a Dockerfile to let people run sessions with them preinstalled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You name it. The more you use it, the more you realize the &lt;strong&gt;enormous potential&lt;/strong&gt; it has.&lt;/p&gt;

&lt;p&gt;Well, show me the money!&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a session
&lt;/h3&gt;

&lt;p&gt;Let’s create a simple Dockerfile that customizes a Deepin Linux base image with a few extra packages:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The hard work is already done in &lt;code&gt;x11docker/deepin&lt;/code&gt;. All we need to do is adding the commands that we would manually run after installing the system ourselves. In this case, a few system tools and a browser.&lt;/p&gt;

&lt;p&gt;Now, let’s create our first session with it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_UDVUOmc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AU7dZHjTDUI_JGFmAiLCFlA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_UDVUOmc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AU7dZHjTDUI_JGFmAiLCFlA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This creates or updates a session named my-workstation&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A Docker image is built from Dockerfile and my-workstation is linked to it&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Run the new session
&lt;/h3&gt;

&lt;p&gt;That’s even simpler:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P3Jo8Bq1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AtTMclnuNOrK1ePAdI3TmRg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P3Jo8Bq1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AtTMclnuNOrK1ePAdI3TmRg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will switch to a new Virtual Terminal and start Deepin in a fraction of the time it would take to boot the entire OS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9_VWQ5-9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2ArDeG1_zQOiFnmpk-JPFYVg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9_VWQ5-9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2ArDeG1_zQOiFnmpk-JPFYVg.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2WIyLeZ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2A-HBmcGpXYvkHocj-jivITg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2WIyLeZ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2A-HBmcGpXYvkHocj-jivITg.png" alt="Deepin Desktop Environment started by DX11"&gt;&lt;/a&gt;&lt;em&gt;Deepin Desktop Environment started by DX11&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Do some work, create some files and log out. Run dx11 run my-workstation again. Your prior work is there, your desktop boots in seconds, your session has the exact software that you need and you can be confident that if something bad happens, you’ll be back to normal when you log back in.&lt;/p&gt;

&lt;p&gt;How about a brand new Gnome session? No problem, let’s extend a Dockerfile from ubuntu:eoan and &lt;a href="https://gitlab.com/brickpop/dx11-nim/blob/master/examples/ubuntu-gnome/Dockerfile"&gt;install a few packages&lt;/a&gt; that we need to use. Two commands (dx11 new and dx11 run) and two minutes later, our DX11 session looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lI8crBFH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2AwiHj3pQu3qOwUUxYbp41FQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lI8crBFH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2AwiHj3pQu3qOwUUxYbp41FQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9b1tbOnL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2Ae198diMQW1wzWuJX_pdwlw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9b1tbOnL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2Ae198diMQW1wzWuJX_pdwlw.png" alt="Gnome Desktop running on Ubuntu 19.10. Started by DX11."&gt;&lt;/a&gt;&lt;em&gt;Gnome Desktop running on Ubuntu 19.10. Started by DX11.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Et voilà!&lt;/em&gt; Would you like to &lt;a href="https://gitlab.com/brickpop/dx11-nim/blob/master/examples/ubuntu-mate/Dockerfile"&gt;do the same with Ubuntu Mate&lt;/a&gt;? No problem. We haven’t touched a single installer yet, but here is our third session in less than 10 minutes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0IJ88yAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2ASMt0WW5LAXHkHUWL6E96zA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0IJ88yAV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/3840/1%2ASMt0WW5LAXHkHUWL6E96zA.png" alt="Mate Desktop started by DX11"&gt;&lt;/a&gt;&lt;em&gt;Mate Desktop started by DX11&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That’s insane!&lt;/p&gt;

&lt;p&gt;The last two screenshots are the result of tweaking the user settings a bit and adding a global menu. But in general, starting a session with an already built image will take less than 10 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attach as root
&lt;/h3&gt;

&lt;p&gt;While my-workstation is running, you can attach to it from your normal session:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FsS0VPnl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ASvcKSHDWBOlJli7-5m_qhA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FsS0VPnl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2ASvcKSHDWBOlJli7-5m_qhA.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will give you a root console, which allows you to try and change whatever you like on the system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you log out of the system, any changes besides /home/user will be &lt;strong&gt;lost&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can &lt;strong&gt;consolidate&lt;/strong&gt; them into your Docker image using docker commit if you like&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  More base images
&lt;/h3&gt;

&lt;p&gt;You can see a few examples provided &lt;a href="https://gitlab.com/brickpop/dx11-nim/blob/master/examples/ubuntu-mate/Dockerfile"&gt;on the DX11 repo&lt;/a&gt; and you can also checkout the &lt;a href="https://hub.docker.com/u/x11docker/#!"&gt;Docker Hub images&lt;/a&gt; contributed by the X11Docker team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contribute
&lt;/h3&gt;

&lt;p&gt;DX11 is far from a finished tool. There are still &lt;a href="https://gitlab.com/brickpop/dx11-nim/issues"&gt;bits and pieces&lt;/a&gt; that need attention, but so far I’m excited to be writing an article like this 😃&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you like it, please give it a try today and provide valuable feedback so we all can benefit from it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Also, feel free to explore these &lt;a href="https://hub.docker.com/u/x11docker/#!"&gt;Docker Hub images&lt;/a&gt;, experiment with your ideal setup and submit &lt;a href="https://gitlab.com/brickpop/dx11-nim/tree/master/examples"&gt;Pull Requests&lt;/a&gt; with your examples&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Future work
&lt;/h3&gt;

&lt;p&gt;The next thing I’d like to see is a “Docker Hub” for desktop sessions. Docker Hub itself would do the trick for the image registry, but most DX11 users would expect screenshots, screencasts, comments, etc.&lt;/p&gt;

&lt;p&gt;That would be a nice challenge to undertake.&lt;/p&gt;

&lt;h2&gt;
  
  
  More conclusions: NIM
&lt;/h2&gt;

&lt;p&gt;In my last article I briefly explained that I was going to check out &lt;a href="https://nim-lang.org/"&gt;the Nim programming language&lt;/a&gt; and see what I could do with it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/brickpop/nim-from-scripting-to-c-code-the-all-in-one-language-4ap6"&gt;&lt;strong&gt;Nim: from scripting to C code. The all-in-one language.&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the experience of building DX11, I’d like to share a few thoughts about it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nim is a nice and clean language&lt;/li&gt;
&lt;li&gt;I’ve felt that the language is well thought out and it leads to writing nice code quickly&lt;/li&gt;
&lt;li&gt;The compiler is fast and produces small, dependency-free binaries. Sometimes, error reporting could be better, but as a newcomer to Nim I’ve been able to quickly detect the root issues and get them fixed&lt;/li&gt;
&lt;li&gt;However, the main drawback is the lack of a graphical debugger for the most common IDE’s. You can use &lt;a href="https://nim-lang.org/blog/2017/10/02/documenting-profiling-and-debugging-nim-code.html"&gt;CLI tools&lt;/a&gt;, but things are likely to be slower when working on complex projects&lt;/li&gt;
&lt;li&gt;Although not as popular as NodeJS’s, Go or Rust, Nim has many libraries available for use on your project, covering the typical dependencies you may find in systems programming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion it’s worth giving it a try. I hope more people get involved so we can tackle bigger and more complex projects with it.&lt;/p&gt;

&lt;p&gt;That’s it for today! I hope you enjoy the &lt;a href="https://gitlab.com/brickpop/dx11-nim"&gt;Christmas toy&lt;/a&gt; 🎁 and if you like it, please clap out loud 👏, show some love ❤️, subscribe and &lt;strong&gt;give DX11 a try&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Until the next time, take care!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G7_cUXIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/20736/0%2AQpTxEsZ9x71OHBjI" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G7_cUXIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/20736/0%2AQpTxEsZ9x71OHBjI" alt="Photo by [Simon Matzinger](https://unsplash.com/@8moments?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)"&gt;&lt;/a&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@8moments?utm_source=medium&amp;amp;utm_medium=referral"&gt;Simon Matzinger&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cover by Mike Meeks on Unsplash&lt;/p&gt;

</description>
      <category>linux</category>
      <category>x11</category>
      <category>docker</category>
      <category>gui</category>
    </item>
    <item>
      <title>Nim: from scripting to C code. The all-in-one language</title>
      <dc:creator>Jør∂¡</dc:creator>
      <pubDate>Sat, 04 Jan 2020 21:54:53 +0000</pubDate>
      <link>https://dev.to/brickpop/nim-from-scripting-to-c-code-the-all-in-one-language-4ap6</link>
      <guid>https://dev.to/brickpop/nim-from-scripting-to-c-code-the-all-in-one-language-4ap6</guid>
      <description>&lt;p&gt;Today I’d like to talk a bit about NIM, a programming language that I have recently discovered.&lt;/p&gt;

&lt;p&gt;You may be used to two different types of languages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Native compiled languages (C, C++, Go, Rust, …)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scripting or interpreted languages (Javascript, Python, Bash, …)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of those have their pro’s and con’s, but today we are here to discover the best of both worlds with &lt;strong&gt;Nim&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  C language as a target
&lt;/h3&gt;

&lt;p&gt;Unlike the typical systems programming languages, Nim allows to compile &lt;strong&gt;&lt;a href="https://nim-lang.org/features.html#native-performance-with-optimisations" rel="noopener noreferrer"&gt;dependency-free static binaries&lt;/a&gt;&lt;/strong&gt; by precompiling your project to &lt;strong&gt;plain old C code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;However, Nim also allows you to compile your projects to &lt;strong&gt;C++&lt;/strong&gt; or &lt;strong&gt;&lt;a href="https://nim-lang.org/features.html#javascript-compilation" rel="noopener noreferrer"&gt;Javascript&lt;/a&gt;&lt;/strong&gt;. This enables to program both backend and &lt;a href="http://picheta.me/snake" rel="noopener noreferrer"&gt;frontend&lt;/a&gt; projects using the same knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Templates and macros are defined using Nim itself
&lt;/h3&gt;

&lt;p&gt;If you are familiar with the typical #define, #ifdef, etc constructs in C, you’ll feel at home with Nim, because the language is &lt;a href="https://nim-lang.org/features.html#small-core-with-extensibility" rel="noopener noreferrer"&gt;flexible enough&lt;/a&gt; to support them by itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Nimscript
&lt;/h3&gt;

&lt;p&gt;When you are targeting internal scripts and automation tasks, you can rely on Nim to do the work for you. Nimscript is a subset of the language that allows to run running custom operations on a more comfortable language. Unlike the traditional shell interpreters, Nim is fully cross-platform and you can opt for a binary or script approach without changing any mental context.&lt;/p&gt;

&lt;p&gt;Personally, I’m excited to play a bit with it and see what it can do 🍭&lt;br&gt;
To me, it looks concise, clean and well thought out. I’ve seen it being used for game development, crypto apps, scripts and more.&lt;/p&gt;

&lt;p&gt;I’m currently working on a first use-case tool that makes use of it on a really cool topic. If all goes smoothly I’ll write a full post about both the language and the “thing” that it will provide.&lt;/p&gt;

&lt;p&gt;Stay tuned for more. &lt;br&gt;
Until then, take care!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F5120%2F0%2A5Ke4jr2HwOjkAb7I" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F5120%2F0%2A5Ke4jr2HwOjkAb7I" alt="Photo by [Conor Luddy](https://unsplash.com/@opticonor?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)"&gt;&lt;/a&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@opticonor?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Conor Luddy&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nim</category>
      <category>language</category>
      <category>native</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Modern makefiles used the right way in 2020</title>
      <dc:creator>Jør∂¡</dc:creator>
      <pubDate>Fri, 27 Dec 2019 12:41:00 +0000</pubDate>
      <link>https://dev.to/brickpop/modern-makefiles-used-the-right-way-in-2020-25mg</link>
      <guid>https://dev.to/brickpop/modern-makefiles-used-the-right-way-in-2020-25mg</guid>
      <description>&lt;p&gt;If you are an experienced developer, you may well know about makefiles. Plain text files defining rules to compile software, back from the old days. Right?&lt;/p&gt;

&lt;p&gt;Today we will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;See the Top 3 myths I’ve encountered on my experience and prove them wrong&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We will see how make shines when used as expected.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Myth #1
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Only useful for C, C++ and native software&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Although it’s true that the C/C++ ecosystem was heavily influenced by the presence of make within the ecosystem, there’s &lt;strong&gt;much more&lt;/strong&gt; that you can do with it. make can handle any kind of file, as long as it has a path and a timestamp.&lt;/p&gt;

&lt;p&gt;The typical example:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Creates a dependency tree of the commands that need to be run on each execution&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you run &lt;code&gt;make edit&lt;/code&gt;, then &lt;code&gt;main.o&lt;/code&gt;, &lt;code&gt;kbd.o&lt;/code&gt; and &lt;code&gt;command.o&lt;/code&gt; are compiled first, and then &lt;code&gt;edit&lt;/code&gt; is built upon them&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, you could also use it to transform something as simple as plain text files:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In this case, our (default) target is &lt;code&gt;my-content.txt&lt;/code&gt; and it is built by simply concatenating the output of two dependent files (created on the fly).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dtrtHTt8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AoNnfD1nhaN1thcUcaCWC3g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dtrtHTt8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2AoNnfD1nhaN1thcUcaCWC3g.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am successfully using it in other scenarios like &lt;strong&gt;web development&lt;/strong&gt; and &lt;strong&gt;mobile app development&lt;/strong&gt;. But there’s no restriction on how it can be used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Myth #2
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s just another task runner, NPM scripts do the same job&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s indeed &lt;strong&gt;not true&lt;/strong&gt;. Yes, it runs tasks (the commands of a rule) but not necessarily. Let’s put the example above with text files.&lt;/p&gt;

&lt;p&gt;When we run &lt;code&gt;make&lt;/code&gt; the first time, it will trigger the dependencies and then the main target. So yes, we run a bunch of tasks. But what happens if we run &lt;code&gt;make&lt;/code&gt; again?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_t--5CAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2APHAJzlV9kw4DpyR1k1js5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_t--5CAR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2000/1%2APHAJzlV9kw4DpyR1k1js5g.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing happens, but why?&lt;/p&gt;

&lt;p&gt;It turns out that &lt;code&gt;make&lt;/code&gt; is designed to &lt;strong&gt;keep track of the modification date of files&lt;/strong&gt;. In this case, it detects that the modification time of &lt;code&gt;dependency-1.txt&lt;/code&gt; and &lt;code&gt;dependency-2.txt&lt;/code&gt; has not change since &lt;code&gt;my-content.txt&lt;/code&gt; was last built. Hence, &lt;code&gt;my-content.txt&lt;/code&gt; does not need to be rebuilt.&lt;/p&gt;

&lt;p&gt;What happens if we change the contents of a dependency?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnWHUX-5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2148/1%2AS9FGa87delczcy1A2m_qCQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZnWHUX-5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2148/1%2AS9FGa87delczcy1A2m_qCQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, make is smart enough to figure out that only the first rule needs to be executed at this point.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This is not the same as what an &lt;code&gt;npm&lt;/code&gt; script would do&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Achieving the same using a shell script would need much more code than a simple &lt;code&gt;makefile&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If each of these 3 rules took 30 seconds to run, you would be saving one minute for yourself on every execution&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Myth #3
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;For web development that’s an overkill tool&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If all you ever do is invoking &lt;code&gt;webpack&lt;/code&gt; then, it is. In the rest of cases, it might not be at all. Put for example, a simple web site with styles, scripts and a static media gallery like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RXq-hJQG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2676/1%2AYkHx7CbFLCmKo2n4mwUHlQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RXq-hJQG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/2676/1%2AYkHx7CbFLCmKo2n4mwUHlQ.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We may want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Instal the NPM dependencies&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Minify the HTML code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transpile Typescript, bundle and minify it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fetch a remote JSON file with data to be imported by Typescript&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Compile sass code into CSS and bundle it&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate the sitemap&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optimize the images and videos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Etc…&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may be thinking of a simple script that would do the trick, run a few commands and the job is done, right? Well, you may get the site built, but at the expense of &lt;strong&gt;building everything every time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Even if you just changed one character, the videos of your web site are going to be transcoded once and again. Even if you have the same styles, &lt;code&gt;sass&lt;/code&gt; is going to launch every time. Even if you have a static site generator and the list of products hasn’t changed, your entire application will be rebuilt from scratch.&lt;/p&gt;

&lt;p&gt;If you care about speed and efficiency, then &lt;code&gt;make&lt;/code&gt; is definitely your friend. But if you only need to launch a few scripts, then make is not the tool you’re looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Top mistakes found when using make
&lt;/h2&gt;

&lt;p&gt;They may be hard to understand if you don’t &lt;a href="https://www.gnu.org/software/make/manual/make.html"&gt;take the time to carefully read the docs&lt;/a&gt;.&lt;br&gt;
It is quite common to see a makefile like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The typical approach is to see the &lt;code&gt;makefile&lt;/code&gt; as a task/subtask tree. When you run make all then, all the dependencies are build. &lt;br&gt;
While this example might eventually work, what are the main issues?&lt;/p&gt;
&lt;h3&gt;
  
  
  Using rules as if they were a simple task
&lt;/h3&gt;

&lt;p&gt;This is more of a conceptual concern, but rules are meant to be evaluated, in order to decide whether the target needs to be built or not.&lt;/p&gt;

&lt;p&gt;However, in the example above &lt;code&gt;markdown:&lt;/code&gt; is being used as an “alias” instead of a rule that prevents useless computation.&lt;/p&gt;
&lt;h3&gt;
  
  
  A rule’s dependency files are not declared
&lt;/h3&gt;

&lt;p&gt;To take advantage of make, the markdown rule should (at the very least) be written like:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Rule names should be bound to actual output files
&lt;/h3&gt;

&lt;p&gt;Using abstractions like &lt;code&gt;all: markup scripts styles media&lt;/code&gt; to make things clean and flexible is fine. However, indirect targets should always link to the specific target file that will fulfill the dependency.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;When defined like this, the modification date of the &lt;strong&gt;dependencies&lt;/strong&gt; and the &lt;strong&gt;target&lt;/strong&gt; file tell make wether the rule needs to run again or not.&lt;/p&gt;

&lt;p&gt;These are seconds that you can save!&lt;/p&gt;

&lt;h3&gt;
  
  
  Variables are there to help
&lt;/h3&gt;

&lt;p&gt;If the list of source files is known beforehand, wouldn’t it be great to use a variable instead of hardcoding the dependencies each time?&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Note that here, the &lt;code&gt;$(MARKUP_FILES)&lt;/code&gt; variable is used to define the dependencies. But it could also be placed on the commands to execute:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Looks good, but we can still do better. Let’s also factorize the &lt;code&gt;sass&lt;/code&gt; executable path as well:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h3&gt;
  
  
  Confusion with make and shell variables
&lt;/h3&gt;

&lt;p&gt;In the example above, note that variables like &lt;code&gt;$(STYLE_FILES)&lt;/code&gt; are &lt;strong&gt;make&lt;/strong&gt; variables. Not shell variables.&lt;/p&gt;

&lt;p&gt;Make variables are evaluated to generate the exact shell command and then, the shell command is executed.&lt;/p&gt;

&lt;p&gt;When writing a command like &lt;code&gt;echo $(PWD)&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;make&lt;/code&gt; will replace &lt;code&gt;$(PWD)&lt;/code&gt; by the current value (i.e.) &lt;code&gt;/home/user&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;bash&lt;/code&gt; will then execute &lt;code&gt;echo /home/user&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not the same as if you run &lt;code&gt;echo $$HOME&lt;/code&gt;. In this case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;make&lt;/code&gt; will replace &lt;code&gt;$$&lt;/code&gt; by &lt;code&gt;$&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;bash&lt;/code&gt; will execute &lt;code&gt;echo $HOME&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use the builtin variables
&lt;/h3&gt;

&lt;p&gt;Still on the same example, we can improve the rule.&lt;/p&gt;

&lt;p&gt;Imagine that &lt;code&gt;index.sass&lt;/code&gt; internally imports other sass files. How do we declare them as dependencies, too?&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Ok, this change needs a bit of explanation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;wildcard&lt;/code&gt; keyword evaluates the glob and puts any matching file path on the variable. So our variable contains a dynamic list of source files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;$@&lt;/code&gt; is evaluated to the name of the target. In this case it is an alias for &lt;code&gt;build/index.css&lt;/code&gt;. Instead of rewriting the own name, we can use this shortcut.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;$&amp;lt;&lt;/code&gt; is evaluated to the first dependency of the rule. We use it because sass takes the entry point, instead of the whole list.&lt;br&gt;
In this case, &lt;code&gt;$&amp;lt;&lt;/code&gt; evaluates to &lt;code&gt;$(STYLE_FILES)&lt;/code&gt; which equals &lt;code&gt;$(wildcard src/index.sass src/styles/*.sass)&lt;/code&gt;. This is the same as passing &lt;code&gt;src/index.sass&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If sass took the whole list of files, then we would write &lt;code&gt;$(SASS) $^ $@&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the command &lt;code&gt;$(SASS) $&amp;lt; $@&lt;/code&gt; would translate into something like:&lt;br&gt;
&lt;code&gt;./node_modules/.bin/sass src/index.sass build/index.css&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Ensure that target folders exist too
&lt;/h3&gt;

&lt;p&gt;If we run the main target as it was, commands would probably complain about the build folder not being present.&lt;/p&gt;

&lt;p&gt;A clean way to ensure its existence would be to create a target for the folder and make targets depend on in before running.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;markup will trigger build first and &lt;code&gt;build/index.html&lt;/code&gt; after.&lt;/p&gt;

&lt;p&gt;We could also use it for our NPM packages. A typical way is to define a &lt;code&gt;make init&lt;/code&gt; static action, but hey… what if this could be automatic?&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Look at this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When &lt;code&gt;node_modules&lt;/code&gt; does not exist (target), the ruler will be triggered.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When &lt;code&gt;package.json&lt;/code&gt; changes (timestamp is newer than &lt;code&gt;node_modules&lt;/code&gt;), the rule will also trigger.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting static actions as Phony
&lt;/h3&gt;

&lt;p&gt;On actions that do not depend on any previous state, a special rule should be used. Typically on actions like make clean you want the command to be triggered, regardless of the current artifacts.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Setting &lt;code&gt;.PHONY&lt;/code&gt; ensures that if the clean rule is matched, it will always execute.&lt;/p&gt;

&lt;p&gt;Why do we need this? Well, imagine that a file named clean is accidentally created on the project. What would happen if we ran &lt;code&gt;make clean&lt;/code&gt;? Well, we would get something like: &lt;code&gt;make:&lt;/code&gt;clean' is up to date` and you would think &lt;em&gt;“fine, it’s clean”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But this message would actually mean: &lt;em&gt;The target file clean already exists and it has no newer dependencies. So, no need to do anything.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you set &lt;code&gt;.PHONY: clean&lt;/code&gt; you ensure that &lt;code&gt;clean&lt;/code&gt; will always run &lt;code&gt;rm -Rf ./build/*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;How would the end &lt;code&gt;makefile&lt;/code&gt; of the example look like?&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;As final remarks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Think of a makefile in a &lt;strong&gt;declarative way&lt;/strong&gt;, not in an imperative way (a bit like a ReactJS component)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Think of rules as statements that transform some input into some output and run only if the source content has changed&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Approach your makefile by looking from the end (the target files, even if they don’t exist yet) and bind any abstract rules to specific output files&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And this wraps it up for today 🎉🎊&lt;br&gt;
I hope you found the article cool and refreshing ❄️🍦 enough to scroll down a bit more and hit the clap 👏👏 button 😃.&lt;/p&gt;

&lt;p&gt;There’s more to come. If you want to stay tuned, don’t hesitate to follow &lt;strong&gt;Stack Me Up&lt;/strong&gt; and new articles like this will be waiting for you next time.&lt;/p&gt;

&lt;p&gt;Until then, take care!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hry61Ilu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/14720/0%2A0W8jmPsVmV4P0toh" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hry61Ilu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/14720/0%2A0W8jmPsVmV4P0toh" alt="Photo by [Sorasak](https://unsplash.com/@boontohhgraphy?utm_source=medium&amp;amp;utm_medium=referral) on [Unsplash](https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral)"&gt;&lt;/a&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@boontohhgraphy?utm_source=medium&amp;amp;utm_medium=referral"&gt;Sorasak&lt;/a&gt; on &lt;a href="https://unsplash.com?utm_source=medium&amp;amp;utm_medium=referral"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>makefile</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Time to self-upgrade</title>
      <dc:creator>Jør∂¡</dc:creator>
      <pubDate>Wed, 18 Dec 2019 11:12:52 +0000</pubDate>
      <link>https://dev.to/brickpop/self-upgrade-2n1j</link>
      <guid>https://dev.to/brickpop/self-upgrade-2n1j</guid>
      <description>&lt;p&gt;My Twitter feed says it all:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3briKmDo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1207086470621810688/4-fmUpbi_normal.jpg" alt="Jordi profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Jordi
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @crunchyspace
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Putting some order into my life 💆‍♂️&lt;br&gt;&lt;br&gt;Time for a dev-centric account. Time to put technology in front and talk about the great things it can do 🖥📱📡&lt;br&gt;&lt;br&gt;&lt;a href="https://twitter.com/hashtag/firstTweet"&gt;#firstTweet&lt;/a&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      00:58 AM - 18 Dec 2019
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1207102710744526850" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1207102710744526850" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1207102710744526850" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      2
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;



&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--3briKmDo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1207086470621810688/4-fmUpbi_normal.jpg" alt="Jordi profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Jordi
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @crunchyspace
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Some things in life can't be systematically postponed, and the future is now.&lt;br&gt;&lt;br&gt;In the coming weeks/months I'm going to spend time on them: &lt;a href="https://twitter.com/hashtag/Travel"&gt;#Travel&lt;/a&gt;, &lt;a href="https://twitter.com/hashtag/Reading"&gt;#Reading&lt;/a&gt;, &lt;a href="https://twitter.com/hashtag/Writing"&gt;#Writing&lt;/a&gt; and even posting some &lt;a href="https://twitter.com/hashtag/Videos"&gt;#Videos&lt;/a&gt;.
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      01:03 AM - 18 Dec 2019
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1207104150879776768" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1207104150879776768" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      0
      &lt;a href="https://twitter.com/intent/like?tweet_id=1207104150879776768" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      1
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  A journey is on the roadmap
&lt;/h1&gt;

&lt;p&gt;As a rather introvert person, I've seldom taken the time to broadcast the highs and lows of my life. However, focusing too much on projects won't make you a better person if you don't devote some time to yourself and to let others know where you are. &lt;/p&gt;

&lt;p&gt;The only constant in life is change and below are the things I'm about to change on the journey I'm about to undertake. &lt;/p&gt;

&lt;h2&gt;
  
  
  Move!
&lt;/h2&gt;

&lt;p&gt;First off, my life partner and I will leave the comfort of our home town and live as remote workers, at least within a reasonable timeframe. The first stop of the journey brings us to Silicon Valley. We want to see how things move, how things breathe and what can we learn from the people that have shaped today's digital society.&lt;/p&gt;

&lt;p&gt;After touring by the West Coast, we'd like to deep dive on the Eastern New York City. Next, Australia might be on the horizon, but the coming weeks will tell where we move next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Talk!
&lt;/h2&gt;

&lt;p&gt;Instead of keeping things for myself and ourselves, I won't hesitate to &lt;a href="https://twitter.com/CrunchySpace"&gt;share thoughts, experiences and findings&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Share!
&lt;/h2&gt;

&lt;p&gt;This one is for tech. I feel so grateful for the insane amount of knowledge and wisdom that I've been given thanks to the internet community. Not only it has made me a better engineer, but also a better and free person. &lt;/p&gt;

&lt;p&gt;I believe that my Karmic debt deserves the time and effort to try to give out a bit of what I've been given myself. &lt;br&gt;
This means that this account will turn into a publication. And it also means, that it will be the basis of all the topics that I'm eager to talk about and tell everyone 😃&lt;/p&gt;

&lt;p&gt;If you spend your life waiting for something to happen, the only thing you'll do is letting your life pass. &lt;/p&gt;

&lt;p&gt;The future is now. Go make it happen. &lt;/p&gt;

&lt;p&gt;Until very soon!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XRqZqPBk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/k5418vhhlj802fko4ewn.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XRqZqPBk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/k5418vhhlj802fko4ewn.jpeg" alt="Colorado"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
