<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>enoent.fr</title>
    <subtitle>Random musings of a software engineer.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://enoent.fr/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://enoent.fr"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2022-09-06T14:00:00+10:00</updated>
    <id>https://enoent.fr/atom.xml</id>
    <entry xml:lang="en">
        <title>Ktor: Altering served content</title>
        <published>2022-09-06T14:00:00+10:00</published>
        <updated>2022-09-06T14:00:00+10:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/ktor-altering-served-content/"/>
        <id>https://enoent.fr/posts/ktor-altering-served-content/</id>
        
        <content type="html" xml:base="https://enoent.fr/posts/ktor-altering-served-content/">&lt;p&gt;When serving files with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ktor.io&quot;&gt;Ktor&lt;&#x2F;a&gt;, there might be times when you need to alter those
files. For example, you might want to inject a script in every served HTML file. For this purpose,
we can leverage &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ktor.io&#x2F;docs&#x2F;plugins.html&quot;&gt;plugins&lt;&#x2F;a&gt;. Plugins can hook at different stages
of the request&#x2F;response pipeline:&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;pre class=&quot;mermaid&quot;&gt;graph LR
    Request --&amp;gt; Plugin1[Plugin]
    Plugin1 --&amp;gt; Handler
    Handler --&amp;gt; Plugin2[Plugin]
    Plugin2 --&amp;gt; Response&lt;&#x2F;pre&gt;
  &lt;figcaption&gt;Ktor request&amp;#x2F;response pipeline&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Let&#x27;s write a plugin that transforms a specific type of files - going with the previous example of
injecting a script to every served HTML file. Our plugin needs to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Take a script URL as an input. If not provided, it won&#x27;t do anything.&lt;&#x2F;li&gt;
&lt;li&gt;Add that script as a &lt;code&gt;&amp;lt;script&amp;gt;&lt;&#x2F;code&gt; element in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;&#x2F;code&gt; of any HTML file served by this Ktor
server.&lt;&#x2F;li&gt;
&lt;li&gt;Obviously, not interfere with any other file format.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s start by defining an empty plugin and its configuration:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;class PluginConfiguration {
  var scriptUrl: String? = null
}

val ScriptInjectionPlugin = createApplicationPlugin(
  name = &amp;quot;ScriptInjectionPlugin&amp;quot;,
  createConfiguration = ::PluginConfiguration,
) {
  val scriptUrl = pluginConfig.scriptUrl

  &#x2F;&#x2F; The rest of our plugin goes here.
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this simple definition, we can add our plugin to a Ktor server:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;embeddedServer(Netty, port = 8080) {
  install(ScriptInjectionPlugin) {
    scriptUrl = &amp;quot;http:&#x2F;&#x2F;foo.bar&#x2F;my&#x2F;injected&#x2F;script.js&amp;quot;
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, let&#x27;s see how we can transform the body. Ktor offers a few handlers to hook into the pipeline
shown above. The main ones are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;onCall&lt;&#x2F;code&gt; is fairly high level, and is mostly useful to get information about a request (e.g. to
log a request).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;onCallReceive&lt;&#x2F;code&gt; allows to transform data received from the client before it&#x27;s processed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;onCallRespond&lt;&#x2F;code&gt; allows to transform data &lt;em&gt;before sending it to the client&lt;&#x2F;em&gt;. That&#x27;s the one we&#x27;re
after.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are a few other handlers for specific use-cases, detailed
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ktor.io&#x2F;docs&#x2F;custom-plugins.html#other&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s hook into &lt;code&gt;onCallRespond&lt;&#x2F;code&gt;, and its helper &lt;code&gt;transformBody&lt;&#x2F;code&gt;. We also need to check if the
content type we&#x27;re sending is HTML, otherwise, we just forward the response body as-is:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;onCallRespond { _ -&amp;gt;
  transformBody { data -&amp;gt;
    if (data is OutgoingContent.ReadChannelContent &amp;amp;&amp;amp;
      data.contentType?.withoutParameters() == ContentType.Text.Html &amp;amp;&amp;amp;
      scriptUrl != null
    ) {
      &#x2F;&#x2F; We are serving an HTML file.
      transform(data, scriptUrl)
    } else {
      data
    }
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can then define our &lt;code&gt;transform()&lt;&#x2F;code&gt; helper, which needs to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Read the body,&lt;&#x2F;li&gt;
&lt;li&gt;Transform it,&lt;&#x2F;li&gt;
&lt;li&gt;Store it into a &lt;code&gt;OutgoingContent&lt;&#x2F;code&gt; for Ktor to continue its pipeline.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;First, let&#x27;s get the injection itself out of the way. Let&#x27;s assume we have our HTML file into a
string, and want a new string with the injected HTML. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jsoup.org&#x2F;&quot;&gt;Jsoup&lt;&#x2F;a&gt; comes in handy for
this kind of operations:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;private fun injectLiveReloadFragment(target: String, scriptUrl: String): String {
  return Jsoup.parse(target).apply {
    head().appendElement(&amp;quot;script&amp;quot;).attributes().add(&amp;quot;src&amp;quot;, scriptUrl)
  }.toString()
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, let&#x27;s actually read and return the body:&lt;&#x2F;p&gt;
&lt;aside class=&quot;note note--note&quot;&gt;
  &lt;div class=&quot;note__title&quot;&gt;Note&lt;&#x2F;div&gt;
  &lt;div class=&quot;note__content&quot;&gt;
    &lt;p&gt;This transformation assumes that the HTML files processed there are fairly small. As such, it
fully reads the channel Ktor provides in memory before transforming it. Any high-traffic server or
longer files should probably &lt;em&gt;not&lt;&#x2F;em&gt; read the content in that way.&lt;&#x2F;p&gt;

  &lt;&#x2F;div&gt;
&lt;&#x2F;aside&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;private suspend fun transform(
  data: OutgoingContent.ReadChannelContent,
  scriptUrl: String,
): OutgoingContent {
  &#x2F;&#x2F; This channel provided by Ktor gives a view of the file about to be returned.
  val channel = data.readFrom()

  &#x2F;&#x2F; Let&amp;#39;s ready everything in the channel into a String.
  val content = StringBuilder().run {
    while (!channel.isClosedForRead) {
      channel.readUTF8LineTo(this)
      append(&amp;#39;\n&amp;#39;)
    }
    toString()
  }

  &#x2F;&#x2F; Inject our script URL.
  val htmlContent = injectLiveReloadFragment(content, scriptUrl)

  &#x2F;&#x2F; Prepare our new content (htmlContent contains it as a string) for Ktor to process.
  return WriterContent(
    body = {
      withContext(Dispatchers.IO) {
        write(htmlContent)
      }
    },
    contentType = ContentType.Text.Html,
  )
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Serving static files with Ktor</title>
        <published>2022-09-03T19:00:00+10:00</published>
        <updated>2022-09-03T19:00:00+10:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/serving-static-files-with-ktor/"/>
        <id>https://enoent.fr/posts/serving-static-files-with-ktor/</id>
        
        <content type="html" xml:base="https://enoent.fr/posts/serving-static-files-with-ktor/">&lt;p&gt;Serving static files with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ktor.io&quot;&gt;Ktor&lt;&#x2F;a&gt; for a static website seems easy enough:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;fun main() {
  embeddedServer(Netty, port = 8080) {
    routing {
      static {
        files(&amp;quot;static-data&amp;quot;)
      }
    }
  }.start(wait = true)
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Done? Not quite. Opening &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost:8080&quot;&gt;http:&#x2F;&#x2F;localhost:8080&lt;&#x2F;a&gt; will only get you a 404,
even if you have an &lt;code&gt;index.html&lt;&#x2F;code&gt; file in the &lt;code&gt;static-data&lt;&#x2F;code&gt; folder. You have to let Ktor know you
want to opt-in serving a default file. Let&#x27;s try again (omitting anything outside the &lt;code&gt;static&lt;&#x2F;code&gt;
block):&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;static {
  files(&amp;quot;static-data&amp;quot;)
  default(&amp;quot;static-data&#x2F;index.html&amp;quot;)
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Surely now this works? Well, kind of. The root folder will have a default &lt;code&gt;index.html&lt;&#x2F;code&gt;, but sadly if
you have any sub-folders with &lt;code&gt;index.html&lt;&#x2F;code&gt; files inside, they won&#x27;t be served by default - that
&lt;code&gt;default()&lt;&#x2F;code&gt; function only works for the top-level folder. Let&#x27;s work around that.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s store our &lt;code&gt;static-data&lt;&#x2F;code&gt; directory into a &lt;code&gt;File&lt;&#x2F;code&gt; variable, replace the &lt;code&gt;files&lt;&#x2F;code&gt; function call by
our own extension. Note that this extension will handle the fallback to &lt;code&gt;index.html&lt;&#x2F;code&gt; in all cases,
so we can also remove the call to &lt;code&gt;default()&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;val staticData = File(&amp;quot;&#x2F;path&#x2F;to&#x2F;static-data&amp;quot;)

static {
  filesWithDefaultIndex(staticData)
}

fun Route.filesWithDefaultIndex(dir: File) {
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In there, we&#x27;ll need a &lt;code&gt;get&lt;&#x2F;code&gt; handler:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;fun Route.filesWithDefaultIndex(dir: File) {
  get(&amp;quot;{static_path...}&amp;quot;) {}
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will catch anything inside the static path, and let us access the requested path through
&lt;code&gt;call.parameters.getAll(&quot;static_path&quot;)&lt;&#x2F;code&gt;. This gets us a list of URL segments that we need to join
with &lt;code&gt;File.separator&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;val relativePath = call.parameters
  .getAll(&amp;quot;static_path&amp;quot;)
  ?.joinToString(File.separator) ?: return@get
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, we have three options:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The requested path points to an existing file. Great! We can just return it.&lt;&#x2F;li&gt;
&lt;li&gt;The requested path points to a folder. If there&#x27;s an &lt;code&gt;index.html&lt;&#x2F;code&gt; file in there, let&#x27;s serve it.&lt;&#x2F;li&gt;
&lt;li&gt;In any other case (the requested path doesn&#x27;t exist, or points to a folder without an &lt;code&gt;index.html&lt;&#x2F;code&gt;
file), we let Ktor handle that (most likely returning a 404).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We can easily access the corresponding local file, and its fallback if it ends up being a directory:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;val combinedDir = staticRootFolder?.resolve(dir) ?: dir
val file = combinedDir.combineSafe(relativePath)
val fallbackFile = file.combineSafe(&amp;quot;index.html&amp;quot;)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now that we have all those defined, here&#x27;s how we take the decision on what to serve:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;val localFile = when {
  file.isFile -&amp;gt; file
  file.isDirectory &amp;amp;&amp;amp; fallbackFile.isFile -&amp;gt; fallbackFile
  else -&amp;gt; return@get
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And finally, we serve the file:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;call.respond(LocalFileContent(localFile, ContentType.defaultForFile(localFile)))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Great! Let&#x27;s see what this looks like all together:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;fun Route.filesWithDefaultIndex(dir: File) {
  val combinedDir = staticRootFolder?.resolve(dir) ?: dir
  get(&amp;quot;{static_path...}&amp;quot;) {
    val relativePath = call.parameters
      .getAll(&amp;quot;static_path&amp;quot;)
      ?.joinToString(File.separator) ?: return@get
    val file = combinedDir.combineSafe(relativePath)
    val fallbackFile = file.combineSafe(&amp;quot;index.html&amp;quot;)

    val localFile = when {
      file.isFile -&amp;gt; file
      file.isDirectory &amp;amp;&amp;amp; fallbackFile.isFile -&amp;gt; fallbackFile
      else -&amp;gt; return@get
    }

    call.respond(LocalFileContent(localFile, ContentType.defaultForFile(localFile)))
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now if you try to open &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost:8080&quot;&gt;http:&#x2F;&#x2F;localhost:8080&lt;&#x2F;a&gt; or
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost:8080&#x2F;foo&quot;&gt;http:&#x2F;&#x2F;localhost:8080&#x2F;foo&lt;&#x2F;a&gt;, both should work (assuming there&#x27;s
&lt;code&gt;index.html&lt;&#x2F;code&gt; files both at the root and in a folder named &lt;code&gt;foo&lt;&#x2F;code&gt;). But if you open
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost:8080&#x2F;foo&#x2F;&quot;&gt;http:&#x2F;&#x2F;localhost:8080&#x2F;foo&#x2F;&lt;&#x2F;a&gt;, with a trailing slash, it still doesn&#x27;t
work. That&#x27;s because Ktor handles the two routes (with and without trailing slash) differently (for
good reasons, see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;youtrack.jetbrains.com&#x2F;issue&#x2F;KTOR-372&quot;&gt;here&lt;&#x2F;a&gt; for some context).&lt;&#x2F;p&gt;
&lt;p&gt;In our case, that&#x27;s not what we want. Luckily, Ktor comes with a plug-in to address this. All that&#x27;s
needed is to install it like any other plugin:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;kotlin&quot;&gt;install(IgnoreTrailingSlash)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And now everything should work as expected.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Writing a Bazel rule set</title>
        <published>2020-05-16T15:55:00+11:00</published>
        <updated>2020-05-16T15:55:00+11:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/creating-a-blog-with-bazel/04-writing-bazel-rule-set/"/>
        <id>https://enoent.fr/posts/creating-a-blog-with-bazel/04-writing-bazel-rule-set/</id>
        
        <content type="html" xml:base="https://enoent.fr/posts/creating-a-blog-with-bazel/04-writing-bazel-rule-set/">&lt;p&gt;This post will cover two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;How to run an arbitrary tool with Bazel (in this case,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;plantuml.com&#x2F;&quot;&gt;PlantUML&lt;&#x2F;a&gt;, a tool to generate diagrams), by writing a
rule set&lt;&#x2F;li&gt;
&lt;li&gt;How to test this rule set.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It should be mentioned that while I was working on this rule set, it became more
and more apparent PlantUML is not a great candidate for this kind of
integration, as its output is platform-dependent (the font rendering). Despite
that, it&#x27;s still a simple tool and as such its integration is simple, albeit not
perfect (the rendering tests I wrote need to run on the same platform every
time).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;plantuml-usage&quot;&gt;PlantUML usage&lt;&#x2F;h2&gt;
&lt;p&gt;PlantUML is a tool that takes a text input looking like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;@startuml
Alice -&amp;gt; Bob: SYN
@enduml
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And outputs an image looking like this:&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;pre class=&quot;mermaid&quot;&gt;sequenceDiagram
    Alice-&amp;gt;&amp;gt;Bob: SYN&lt;&#x2F;pre&gt;
  &lt;figcaption&gt;PlantUML sample output&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;PlantUML has multiple way of being invoked (CLI, GUI, as well as a &lt;em&gt;lot&lt;&#x2F;em&gt; of
integrations with different tools), but we&#x27;ll go with the easiest: a one-shot
CLI invocation. It takes as inputs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A text file, representing a diagram&lt;&#x2F;li&gt;
&lt;li&gt;An optional configuration file, giving control over the output&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It then outputs a single image file, which can be of different formats (we&#x27;ll
just cover SVG and PNG in this article, but adding support for other formats is
trivial).&lt;&#x2F;p&gt;
&lt;p&gt;PlantUML ships as a JAR file, which needs to be run with Java. An invocation
generating the sample image above would look like that:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;java -jar plantuml.jar -tpng -p &amp;lt; &amp;#39;mysource.puml&amp;#39; &amp;gt; &amp;#39;dir&#x2F;myoutput.png&amp;#39;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pretty straightforward: run the JAR, with a single option for the image type,
pipe the content of the input file and get the output file back. The &lt;code&gt;-p&lt;&#x2F;code&gt; flag
is the short form of &lt;code&gt;-pipe&lt;&#x2F;code&gt;, which we&#x27;re using as using pipes is the only way
of properly controlling the output path (without that, PlantUML tries to be
smart and places the output next to the input).&lt;&#x2F;p&gt;
&lt;p&gt;With a configuration file:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;java -jar plantuml.jar -tpng -config config.puml -p &amp;lt; &amp;#39;mysource.puml&amp;#39; &amp;gt; &amp;#39;dir&#x2F;myoutput.png&amp;#39;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Simple enough, right? Well, not really. PlantUML actually integrates some
metadata in the files it generates. For example, when generating an SVG:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;svg&quot;&gt;&amp;lt;!-- The actual SVG image has been omitted, as this part is deterministic and
pretty long. --&amp;gt;

&amp;lt;svg&amp;gt;&amp;lt;g&amp;gt;
&amp;lt;!--MD5=[8d4298e8c40046c92682b92efe1f786e]
@startuml
Alice -&amp;gt; Bob: SYN
@enduml

PlantUML version 1.2020.07(Sun Apr 19 21:42:40 AEST 2020)
(GPL source distribution)
Java Runtime: OpenJDK Runtime Environment
JVM: OpenJDK 64-Bit Server VM
Java Version: 11.0.6+10
Operating System: Linux
Default Encoding: UTF-8
Language: en
Country: AU
--&amp;gt;&amp;lt;&#x2F;g&amp;gt;&amp;lt;&#x2F;svg&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This makes PlantUML non hermetic by default (in addition to the fonts issue
mentioned earlier). While PlantUML has a  simple way of working around that (in
the form of a &lt;code&gt;-nometadata&lt;&#x2F;code&gt; flag), this is something to keep in mind when
integrating a tool with Bazel: is this tool usable in a hermetic way? If not,
how to minimise the impact of this non-hermeticity?&lt;&#x2F;p&gt;
&lt;p&gt;From there, here is the invocation we&#x27;ll work with:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;java -jar plantuml.jar -tpng -nometadata -config config.puml \
  -p &amp;lt; &amp;#39;mysource.puml&amp;#39; &amp;gt; &amp;#39;dir&#x2F;myoutput.png&amp;#39;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;getting-plantuml&quot;&gt;Getting PlantUML&lt;&#x2F;h2&gt;
&lt;p&gt;PlantUML is a Java application, available as a JAR on Maven. As such, it can be
fetched with the help of
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_jvm_external&#x2F;&quot;&gt;rules_jvm_external&lt;&#x2F;a&gt;, as was
explained in
&lt;a href=&quot;https:&#x2F;&#x2F;enoent.fr&#x2F;posts&#x2F;creating-a-blog-with-bazel&#x2F;02-compiling-a-kotlin-application-with-bazel&#x2F;#dependencies&quot;&gt;a previous article&lt;&#x2F;a&gt;.
The Maven rules will expose the JAR as a library, but we need a binary to be
able to run it. In e.g. &lt;code&gt;&#x2F;&#x2F;third_party&#x2F;plantuml&#x2F;BUILD&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;@rules_java&#x2F;&#x2F;java:defs.bzl&amp;quot;, &amp;quot;java_binary&amp;quot;)

java_binary(
    name = &amp;quot;plantuml&amp;quot;,
    main_class = &amp;quot;net.sourceforge.plantuml.Run&amp;quot;,
    visibility = [&amp;quot;&#x2F;&#x2F;visibility:public&amp;quot;],
    runtime_deps = [
        &amp;quot;@maven&#x2F;&#x2F;:net_sourceforge_plantuml_plantuml&amp;quot;,
    ],
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;From there, we can use &lt;code&gt;&#x2F;&#x2F;third_party&#x2F;plantuml&lt;&#x2F;code&gt; as any Bazel binary target - we
can run it with &lt;code&gt;bazel run&lt;&#x2F;code&gt;, and we can pass it as a tool for rule actions.&lt;&#x2F;p&gt;
&lt;p&gt;This is a pattern that works well for any JVM-based tool. Other kinds of tools
will need a different preparation step to make them available through Bazel -
but as long as you can get a binary, you should be good.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rule-set-structure&quot;&gt;Rule set structure&lt;&#x2F;h2&gt;
&lt;p&gt;This rule set will follow the same structure we previously used for
&lt;a href=&quot;https:&#x2F;&#x2F;enoent.fr&#x2F;posts&#x2F;creating-a-blog-with-bazel&#x2F;02-compiling-a-kotlin-application-with-bazel&#x2F;#ktlint&quot;&gt;Ktlint&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Based in &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;A public interface exposed in &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;defs.bzl&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Internal actions definition in &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal&#x2F;actions.bzl&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Internal rule definition in &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal&#x2F;rules.bzl&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But in addition:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Tests for the actions in &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal&#x2F;actions_test.bzl&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Integration tests in &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;tests&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s start by defining our actions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;actions&quot;&gt;Actions&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;&#x2F;h3&gt;
&lt;p&gt;We need only one action for our rule: one that takes a source file, an optional
configuration file, the PlantUML binary, and emits the output file by calling
PlantUML. Let&#x27;s assume for a moment we have a helper function which, given the
proper input, returns the PlantUML command line to call, called
&lt;code&gt;plantuml_command_line&lt;&#x2F;code&gt;, and write the action from there:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def plantuml_generate(ctx, src, format, config, out):
    &amp;quot;&amp;quot;&amp;quot;Generates a single PlantUML graph from a puml file.

    Args:
        ctx: analysis context.
        src: source file to be read.
        format: the output image format.
        config: the configuration file. Optional.
        out: output image file.
    &amp;quot;&amp;quot;&amp;quot;
    command = plantuml_command_line(
        executable = ctx.executable._plantuml_tool.path,
        config = config.path if config else None,
        src = src.path,
        output = out.path,
        output_format = format,
    )

    inputs = [src]

    if config:
        inputs.append(config)

    ctx.actions.run_shell(
        outputs = [out],
        inputs = inputs,
        tools = [ctx.executable._plantuml_tool],
        command = command,
        mnemonic = &amp;quot;PlantUML&amp;quot;,
        progress_message = &amp;quot;Generating %s&amp;quot; % out.basename,
    )
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is pretty straightforward: we generate the command line, passing either the
attributes&#x27; respective paths (or &lt;code&gt;None&lt;&#x2F;code&gt; for the configuration file if it&#x27;s not
provided, since it&#x27;s optional), as well as the requested image format. We define
that both our source file and configuration files are inputs, and PlantUML is a
requested tool.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s implement our helper function. It&#x27;s there again really
straightforward: it gets a bunch of paths as input, and needs to generate a
command line call (in the form of a simple string) from them:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def plantuml_command_line(executable, config, src, output, output_format):
    &amp;quot;&amp;quot;&amp;quot;Formats the command line to call PlantUML with the given arguments.

    Args:
        executable: path to the PlantUML binary.
        config: path to the configuration file. Optional.
        src: path to the source file.
        output: path to the output file.
        output_format: image format of the output file.

    Returns:
        A command to invoke PlantUML
    &amp;quot;&amp;quot;&amp;quot;

    command = &amp;quot;%s -nometadata -p -t%s &amp;quot; % (
        shell.quote(executable),
        output_format,
    )

    if config:
        command += &amp;quot; -config %s &amp;quot; % shell.quote(config)

    command += &amp;quot; &amp;lt; %s &amp;gt; %s&amp;quot; % (
        shell.quote(src),
        shell.quote(output),
    )

    return command
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;An interesting note is that because PlantUML is already integrated as an
executable Bazel target, we don&#x27;t care that it&#x27;s a JAR, a C++ binary or a shell
script: Bazel knows exactly what this executable is made of, how to prepare
(e.g. compile) it if necessary, its runtime dependencies (in this case, a JRE)
and, more importantly in this context, how to run it. We can treat our tool
target as a single executable file, and run it as such just from its path.
Bazel will automatically make sure to provide us with everything we need. (For
more details: the target actually points to a shell script generated by Bazel,
through the Java rules, which in the case of a &lt;code&gt;java_binary&lt;&#x2F;code&gt; target is
responsible for defining the classpath, among other things. The JAR file is
merely a dependency of this shell script, and as such is provided as a runtime
dependency.)&lt;&#x2F;p&gt;
&lt;p&gt;Writing this as a helper function rather than directly in the action definition
serves two purposes: not only does it make the whole thing slightly easier to
read, but this function, which contains the logic (even though in this case it&#x27;s
really simple), is easily testable: it takes only strings as arguments, and
returns a string. It&#x27;s also a pure function: it doesn&#x27;t have any side effect,
and as such it will always return the same output given the same set of inputs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tests&quot;&gt;Tests&lt;&#x2F;h3&gt;
&lt;p&gt;To test Starlark functions like this one, Bazel&#x27;s
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;bazel-skylib&quot;&gt;Skylib&lt;&#x2F;a&gt; provides a test framework
which, while requiring a bit of boilerplate, is pretty simple to use. In this
specific case, we only have two different cases to test: with and without
configuration file provided. Error cases should be unreachable due to the way
the rule will be defined: Bazel will be responsible for enforcing the presence
of an executable target for PlantUML&#x27;s binary, a valid image format... Let&#x27;s see
how that works. In &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal&#x2F;actions_test.bzl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;&amp;quot;&amp;quot;&amp;quot;Unit tests for PlantUML action&amp;quot;&amp;quot;&amp;quot;

load(&amp;quot;@bazel_skylib&#x2F;&#x2F;lib:unittest.bzl&amp;quot;, &amp;quot;asserts&amp;quot;, &amp;quot;unittest&amp;quot;)
load(&amp;quot;:actions.bzl&amp;quot;, &amp;quot;plantuml_command_line&amp;quot;)

def _no_config_impl(ctx):
    env = unittest.begin(ctx)
    asserts.equals(
        env,
        &amp;quot;&amp;#39;&#x2F;bin&#x2F;plantuml&amp;#39; -nometadata -p -tpng  &amp;lt; &amp;#39;mysource.puml&amp;#39; &amp;gt; &amp;#39;dir&#x2F;myoutput.png&amp;#39;&amp;quot;,
        plantuml_command_line(
            executable = &amp;quot;&#x2F;bin&#x2F;plantuml&amp;quot;,
            config = None,
            src = &amp;quot;mysource.puml&amp;quot;,
            output = &amp;quot;dir&#x2F;myoutput.png&amp;quot;,
            output_format = &amp;quot;png&amp;quot;,
        ),
    )
    return unittest.end(env)

no_config_test = unittest.make(_no_config_impl)

def _with_config_impl(ctx):
    env = unittest.begin(ctx)
    asserts.equals(
        env,
        &amp;quot;&amp;#39;&#x2F;bin&#x2F;plantuml&amp;#39; -nometadata -p -tpng  -config &amp;#39;myskin.skin&amp;#39;  &amp;lt; &amp;#39;mysource.puml&amp;#39; &amp;gt; &amp;#39;dir&#x2F;myoutput.png&amp;#39;&amp;quot;,
        plantuml_command_line(
            executable = &amp;quot;&#x2F;bin&#x2F;plantuml&amp;quot;,
            config = &amp;quot;myskin.skin&amp;quot;,
            src = &amp;quot;mysource.puml&amp;quot;,
            output = &amp;quot;dir&#x2F;myoutput.png&amp;quot;,
            output_format = &amp;quot;png&amp;quot;,
        ),
    )
    return unittest.end(env)

with_config_test = unittest.make(_with_config_impl)

def actions_test_suite():
    unittest.suite(
        &amp;quot;actions_tests&amp;quot;,
        no_config_test,
        with_config_test,
    )
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First, we define two functions, which are the actual test logic:
&lt;code&gt;_no_config_impl&lt;&#x2F;code&gt; and &lt;code&gt;_with_config_impl&lt;&#x2F;code&gt;. Their content is pretty simple: we
start a unit test environment, we invoke our test function and assert that the
result is indeed what we expected, and we close the unit test environment. The
return value is needed by the test framework, as it&#x27;s what carries what
assertions passed or failed.&lt;&#x2F;p&gt;
&lt;p&gt;Next, we declare those two functions as actual unit tests, wrapping them with a
call to &lt;code&gt;unittest.make&lt;&#x2F;code&gt;. We can then add those two test targets to a test suite,
which is what actually generates a test target when invoked. Which means that
this macro needs to be invoked, in the &lt;code&gt;BUILD&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;:actions_test.bzl&amp;quot;, &amp;quot;actions_test_suite&amp;quot;)

actions_test_suite()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can run our tests, and hopefully everything should pass:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;$ bazel test &#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal:actions_tests
INFO: Invocation ID: 112bd049-7398-4b23-b62b-1398e9731eb7
INFO: Analyzed 2 targets (5 packages loaded, 927 targets configured).
INFO: Found 2 test targets...
INFO: Elapsed time: 0.238s, Critical Path: 0.00s
INFO: 0 processes.
&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal:actions_tests_test_0                           PASSED in 0.4s
&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal:actions_tests_test_1                           PASSED in 0.3s

Executed 0 out of 2 tests: 2 tests pass.
INFO: Build completed successfully, 1 total action
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;rules-definition&quot;&gt;Rules definition&lt;&#x2F;h2&gt;
&lt;p&gt;Similarly as the actions definition, we only have one rule to define here. Let&#x27;s
call it &lt;code&gt;plantuml_graph()&lt;&#x2F;code&gt;. It needs our usual set of inputs, and outputs a
single file, which name will be &lt;code&gt;${target_name}.{image_format}&lt;&#x2F;code&gt;. It&#x27;s also where
we define the set of acceptable image formats, the fact that the input file is
mandatory but the configuration file optional, and the actual executable target
to use for PlantUML. The only thing we actually do is, as expected, calling our
&lt;code&gt;plantuml_generate&lt;&#x2F;code&gt; action defined above.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(
    &amp;quot;:actions.bzl&amp;quot;,
    &amp;quot;plantuml_generate&amp;quot;,
)

def _plantuml_graph_impl(ctx):
    output = ctx.actions.declare_file(&amp;quot;{name}.{format}&amp;quot;.format(
        name = ctx.label.name,
        format = ctx.attr.format,
    ))
    plantuml_generate(
        ctx,
        src = ctx.file.src,
        format = ctx.attr.format,
        config = ctx.file.config,
        out = output,
    )

    return [DefaultInfo(
        files = depset([output]),
    )]

plantuml_graph = rule(
    _plantuml_graph_impl,
    attrs = {
        &amp;quot;config&amp;quot;: attr.label(
            doc = &amp;quot;Configuration file to pass to PlantUML. Useful to tweak the skin&amp;quot;,
            allow_single_file = True,
        ),
        &amp;quot;format&amp;quot;: attr.string(
            doc = &amp;quot;Output image format&amp;quot;,
            default = &amp;quot;png&amp;quot;,
            values = [&amp;quot;png&amp;quot;, &amp;quot;svg&amp;quot;],
        ),
        &amp;quot;src&amp;quot;: attr.label(
            allow_single_file = [&amp;quot;.puml&amp;quot;],
            doc = &amp;quot;Source file to generate the graph from&amp;quot;,
            mandatory = True,
        ),
        &amp;quot;_plantuml_tool&amp;quot;: attr.label(
            default = &amp;quot;&#x2F;&#x2F;third_party&#x2F;plantuml&amp;quot;,
            executable = True,
            cfg = &amp;quot;host&amp;quot;,
        ),
    },
    outputs = {
        &amp;quot;graph&amp;quot;: &amp;quot;%{name}.%{format}&amp;quot;,
    },
    doc = &amp;quot;Generates a PlantUML graph from a puml file&amp;quot;,
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;public-interface&quot;&gt;Public interface&lt;&#x2F;h2&gt;
&lt;p&gt;As we only have a single rule, and nothing else specific to do, the public
interface is dead simple:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal:rules.bzl&amp;quot;, _plantuml_graph = &amp;quot;plantuml_graph&amp;quot;)

plantuml_graph = _plantuml_graph
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You might then be wondering: why is this useful, and why shouldn&#x27;t I just import
the rule definition from &lt;code&gt;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal:rules.bzl&lt;&#x2F;code&gt; directly? Having
this kind of public interface allows you to tweak the actual rule definition
without breaking any consumer site, as long as you respect the public interface.
You can also add features to every consumer site in a really simple way. Let&#x27;s
imagine for example that you have a &lt;code&gt;view_image&lt;&#x2F;code&gt; rule which, given an image
file, generates a script to view it, you could then transform your public
interface like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;&#x2F;&#x2F;tools&#x2F;plantuml&#x2F;internal:rules.bzl&amp;quot;, _plantuml_graph = &amp;quot;plantuml_graph&amp;quot;)
load(&amp;quot;&#x2F;&#x2F;tools&#x2F;utils:defs.bzl&amp;quot;, _view_image = &amp;quot;view_image&amp;quot;)

def plantuml_graph(name, src, config, format):
    _plantuml_graph(
        name = name,
        src = src,
        config = config,
        format = format,
    )

    _view_image(
        name = &amp;quot;%s.view&amp;quot; % name,
        src = &amp;quot;:%s.%s&amp;quot; % (name, format),
    )
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And suddenly, all your PlantUML graphs have an implicit &lt;code&gt;.view&lt;&#x2F;code&gt; target defined
automatically, allowing you to see the output directly without having to dig in
Bazel&#x27;s output directories.&lt;&#x2F;p&gt;
&lt;p&gt;A set of Bazel rules for LaTeX actually provides such a feature to view the PDF
output: they have a
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ProdriveTechnologies&#x2F;bazel-latex&#x2F;blob&#x2F;master&#x2F;view_pdf.sh&quot;&gt;&lt;code&gt;view_pdf.sh&lt;&#x2F;code&gt; script&lt;&#x2F;a&gt;,
used by  their main
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ProdriveTechnologies&#x2F;bazel-latex&#x2F;blob&#x2F;master&#x2F;latex.bzl#L45&quot;&gt;&lt;code&gt;latex_document&lt;&#x2F;code&gt; macro&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;further-testing&quot;&gt;Further testing&lt;&#x2F;h2&gt;
&lt;p&gt;For a rule this simple, I took just a simple further step: having a few
reference PlantUML graphs, as well as their expected rendered output, which I
compare through Phosphorus, a really simple tool I wrote to help compare two
images, covered in the previous article (I told you it would be useful!). But
for more complex cases, Skylib offer more utilities like an
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;bazel-skylib&#x2F;blob&#x2F;master&#x2F;docs&#x2F;analysis_test_doc.md&quot;&gt;analysis test&lt;&#x2F;a&gt;,
and a
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;bazel-skylib&#x2F;blob&#x2F;master&#x2F;docs&#x2F;build_test_doc.md&quot;&gt;build test&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;While writing this kind of tools might look like a lot of works, it&#x27;s actually
pretty mechanical for a lot of cases. I worked on a few others like
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;igorshubovych&#x2F;markdownlint-cli&quot;&gt;markdownlint&lt;&#x2F;a&gt;, which now
runs on all my Markdown files as regular Bazel test targets, or
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pmt.sourceforge.io&#x2F;pngcrush&#x2F;&quot;&gt;pngcrush&lt;&#x2F;a&gt;, which is ran on the PNG files
hosted on this blog. In a monorepo, writing such a rule is the kind of task that
you do once, and it just keeps on giving - you can easily compose different
rules with a main use-case, with a bunch of test targets generated for virtually
free.&lt;&#x2F;p&gt;
&lt;p&gt;On another note, I&#x27;m aware that having all this in a public repository would
make things much simpler to follow. Sadly, it&#x27;s part of a larger mono-repository
which makes open-sourcing only the relevant parts tricky. Dumping a snapshot
somewhere would be an option, but I&#x27;d rather have an actual living repository.&lt;&#x2F;p&gt;
&lt;p&gt;Now that we have all the tools we need (that was kind of convoluted, I&#x27;ll give
you that), there are only two steps left to cover:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Generating the actual blog (ironically enough, this will be a really quick
step, despite being the only really important one)&lt;&#x2F;li&gt;
&lt;li&gt;Managing the deployment.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We&#x27;re getting there!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Compiling a Kotlin application with Bazel</title>
        <published>2019-12-08T11:30:00+11:00</published>
        <updated>2019-12-08T11:30:00+11:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/creating-a-blog-with-bazel/02-compiling-a-kotlin-application-with-bazel/"/>
        <id>https://enoent.fr/posts/creating-a-blog-with-bazel/02-compiling-a-kotlin-application-with-bazel/</id>
        
        <content type="html" xml:base="https://enoent.fr/posts/creating-a-blog-with-bazel/02-compiling-a-kotlin-application-with-bazel/">&lt;p&gt;This post will describe how to compile a small application written in Kotlin
using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bazel.build&quot;&gt;Bazel&lt;&#x2F;a&gt;, tests, as well as how to use static
analyzers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;phosphorus&quot;&gt;Phosphorus&lt;&#x2F;h2&gt;
&lt;p&gt;Phosphorus is the application that this post will cover. It&#x27;s a small utility
that I wrote to check if an image matches a reference. If it doesn&#x27;t, Phosphorus
generates an image highlighting the differences. The goal is to be able to check
that something generates an image in a given way, and doesn&#x27;t change - at least
if it&#x27;s not expected. The actual usage will be covered later in this series.
While it&#x27;s not open-source yet, it&#x27;s something I intend to do at some point.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s written in Kotlin, as a couple external dependencies (
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ajalt.github.io&#x2F;clikt&#x2F;&quot;&gt;Clikt&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dagger.dev&#x2F;&quot;&gt;Dagger&lt;&#x2F;a&gt;), as
well as a few tests. This is the structure:&lt;&#x2F;p&gt;
&lt;figure&gt;
  &lt;pre class=&quot;mermaid&quot;&gt;classDiagram
    namespace loader {
        class ImageLoader {
            &amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
        }
        class ImageIoLoader {
        }
    }

    namespace differ {
        class ImageDiffer {
            &amp;lt;&amp;lt;interface&amp;gt;&amp;gt;
        }
        class ImageDifferImpl {
        }
    }

    namespace data {
        class Image
        class DiffResult
    }

    class Phosphorus

    ImageIoLoader ..|&amp;gt; ImageLoader
    ImageDifferImpl ..|&amp;gt; ImageDiffer
    Phosphorus --&amp;gt; ImageLoader
    Phosphorus --&amp;gt; ImageDiffer&lt;&#x2F;pre&gt;
  &lt;figcaption&gt;Phosphorus&amp;#x27;s class diagram&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The &lt;code&gt;differ&lt;&#x2F;code&gt; module contains the core logic - comparing two images, and
generating a &lt;code&gt;DiffResult&lt;&#x2F;code&gt;. This &lt;code&gt;DiffResult&lt;&#x2F;code&gt; contains both the straightforward
result of the comparison (are the two images identical?) and an image
highlighting the differences, if any. The &lt;code&gt;loader&lt;&#x2F;code&gt; package is responsible for
loading and writing images. Finally, the &lt;code&gt;Phosphorus&lt;&#x2F;code&gt; class orchestrates all
that, in addition to processing command line arguments with Clikt.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dependencies&quot;&gt;Dependencies&lt;&#x2F;h2&gt;
&lt;p&gt;Phosphorus has two dependencies: Clikt, and Dagger. Both of them are available
as Maven artifacts. In order to pull Maven artifacts, the Bazel team provides a
set of rules called
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_jvm_external&#x2F;&quot;&gt;rules_jvm_external&lt;&#x2F;a&gt;. The
idea is the following: you list a bunch of Maven coordinates and repositories,
the rule will fetch all of them (and their transitive dependencies) during the
loading phase, and generate Bazel targets corresponding to those Maven
artifacts, on which you can depend. Let&#x27;s see how we can use them. The first
step is to load the rules, in the &lt;code&gt;WORKSPACE&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;@bazel_tools&#x2F;&#x2F;tools&#x2F;build_defs&#x2F;repo:http.bzl&amp;quot;, &amp;quot;http_archive&amp;quot;)

http_archive(
    name = &amp;quot;rules_jvm_external&amp;quot;,
    sha256 = &amp;quot;62133c125bf4109dfd9d2af64830208356ce4ef8b165a6ef15bbff7460b35c3a&amp;quot;,
    strip_prefix = &amp;quot;rules_jvm_external-3.0&amp;quot;,
    url = &amp;quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_jvm_external&#x2F;archive&#x2F;3.0.zip&amp;quot;,
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, we can load and call &lt;code&gt;maven_install&lt;&#x2F;code&gt; with the list of Maven coordinates we
want, in the &lt;code&gt;WORKSPACE&lt;&#x2F;code&gt; too:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;@rules_jvm_external&#x2F;&#x2F;:defs.bzl&amp;quot;, &amp;quot;maven_install&amp;quot;)

maven_install(
    artifacts = [
        &amp;quot;com.github.ajalt:clikt:2.2.0&amp;quot;,
        &amp;quot;com.google.dagger:dagger:2.25.2&amp;quot;,
        &amp;quot;com.google.dagger:dagger-compiler:2.25.2&amp;quot;,
        &amp;quot;com.google.truth:truth:1.0&amp;quot;,
        &amp;quot;javax.inject:javax.inject:1&amp;quot;,
        &amp;quot;junit:junit:4.12&amp;quot;,
    ],
    fetch_sources = True,
    repositories = [
        &amp;quot;https:&#x2F;&#x2F;maven.google.com&amp;quot;,
        &amp;quot;https:&#x2F;&#x2F;repo1.maven.org&#x2F;maven2&amp;quot;,
        &amp;quot;https:&#x2F;&#x2F;jcenter.bintray.com&#x2F;&amp;quot;,
    ],
    strict_visibility = True,
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A couple of things to note:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We&#x27;re also downloading &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;junit.org&#x2F;junit4&#x2F;&quot;&gt;JUnit&lt;&#x2F;a&gt; and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;truth.dev&#x2F;&quot;&gt;Truth&lt;&#x2F;a&gt;, that we&#x27;re going to use in tests&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;maven_install&lt;&#x2F;code&gt; can try to download the sources, if they&#x27;re available on
Maven, to be able to see them directly from the IDE&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;At this point, Clikt, JUnit and Truth are ready to be used. They are exposed
respectively as &lt;code&gt;@maven&#x2F;&#x2F;:com_github_ajalt_clikt&lt;&#x2F;code&gt;, &lt;code&gt;@maven&#x2F;&#x2F;:junit_junit&lt;&#x2F;code&gt; and
&lt;code&gt;@maven&#x2F;&#x2F;:com_google_truth_truth&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Dagger, on the other hand, comes with an annotation processor and, as such,
needs some more work: it needs to be exposed as a Java Plugin. Because it&#x27;s a
third party dependency, this will be defined in &lt;code&gt;&#x2F;&#x2F;third_party&#x2F;dagger&#x2F;BUILD&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;java_plugin(
    name = &amp;quot;dagger_plugin&amp;quot;,
    processor_class = &amp;quot;dagger.internal.codegen.ComponentProcessor&amp;quot;,
    deps = [
        &amp;quot;@maven&#x2F;&#x2F;:com_google_dagger_dagger_compiler&amp;quot;,
    ],
)

java_library(
    name = &amp;quot;dagger&amp;quot;,
    exported_plugins = [&amp;quot;:dagger_plugin&amp;quot;],
    visibility = [&amp;quot;&#x2F;&#x2F;visibility:public&amp;quot;],
    exports = [
        &amp;quot;@maven&#x2F;&#x2F;:com_google_dagger_dagger&amp;quot;,
        &amp;quot;@maven&#x2F;&#x2F;:com_google_dagger_dagger_compiler&amp;quot;,
        &amp;quot;@maven&#x2F;&#x2F;:javax_inject_javax_inject&amp;quot;,
    ],
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It can now be used as &lt;code&gt;&#x2F;&#x2F;third_party&#x2F;dagger&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compilation&quot;&gt;Compilation&lt;&#x2F;h2&gt;
&lt;p&gt;Bazel doesn&#x27;t support Kotlin out of the box (the few languages natively
supported, Java and C++, are currently getting extracted from Bazel&#x27;s core, so
all languages will soon share a similar integration). In order to compile some
Kotlin code, we&#x27;ll have to use some Starlark rules describing how to use
&lt;code&gt;kotlinc&lt;&#x2F;code&gt;. A set of rules is available
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_kotlin&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;. While they don&#x27;t support
Kotlin&#x2F;Native, they do support targeting both the JVM (including Android) and
JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;In order to use those rules, we need to declare them in the &lt;code&gt;WORKSPACE&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;@bazel_tools&#x2F;&#x2F;tools&#x2F;build_defs&#x2F;repo:http.bzl&amp;quot;, &amp;quot;http_archive&amp;quot;)

http_archive(
    name = &amp;quot;io_bazel_rules_kotlin&amp;quot;,
    sha256 = &amp;quot;54678552125753d9fc0a37736d140f1d2e69778d3e52cf454df41a913b964ede&amp;quot;,
    strip_prefix = &amp;quot;rules_kotlin-legacy-1.3.0-rc3&amp;quot;,
    url = &amp;quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_kotlin&#x2F;archive&#x2F;legacy-1.3.0-rc3.zip&amp;quot;,
)

load(&amp;quot;@io_bazel_rules_kotlin&#x2F;&#x2F;kotlin:kotlin.bzl&amp;quot;, &amp;quot;kotlin_repositories&amp;quot;, &amp;quot;kt_register_toolchains&amp;quot;)

kotlin_repositories()

kt_register_toolchains()
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once that&#x27;s done, we have access to a few rules:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;kt_js_library&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;kt_js_import&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;kt_jvm_binary&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;kt_jvm_import&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;kt_jvm_library&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;kt_jvm_test&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;kt_android_library&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We&#x27;re going to use &lt;code&gt;kt_jvm_binary&lt;&#x2F;code&gt;, &lt;code&gt;kt_jvm_library&lt;&#x2F;code&gt; as well as &lt;code&gt;kt_jvm_test&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As JVM-based languages have a strong correlation between packages and folder
structure, we need to be careful about where we store our source code. Bazel
handles a few names as potential Java &quot;roots&quot;: &lt;code&gt;java&lt;&#x2F;code&gt;, &lt;code&gt;javatests&lt;&#x2F;code&gt; and &lt;code&gt;src&lt;&#x2F;code&gt;.
Anything inside a directory named like this needs to follow the package&#x2F;folder
correlation. For example, a class
&lt;code&gt;fr.enoent.phosphorus.client.matcher.Phosphorus&lt;&#x2F;code&gt; can be stored at those
locations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;Phosphorus.kt&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;&#x2F;tools&#x2F;images&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;Phosphorus.kt&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;&#x2F;&#x2F;java&#x2F;tools&#x2F;images&#x2F;src&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;Phosphorus.kt&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In my repo, everything Java-related is stored under &lt;code&gt;&#x2F;&#x2F;java&lt;&#x2F;code&gt;, and the
corresponding tests are in &lt;code&gt;&#x2F;&#x2F;javatests&lt;&#x2F;code&gt; (following the same structure).
Phosphorus will hence be in &lt;code&gt;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see how we can define a simple Kotlin library, with the &lt;code&gt;data&lt;&#x2F;code&gt; module. In
&lt;code&gt;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;data&#x2F;BUILD&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;@io_bazel_rules_kotlin&#x2F;&#x2F;kotlin:kotlin.bzl&amp;quot;, &amp;quot;kt_jvm_library&amp;quot;)

kt_jvm_library(
    name = &amp;quot;data&amp;quot;,
    srcs = [
        &amp;quot;DiffResult.kt&amp;quot;,
        &amp;quot;Image.kt&amp;quot;,
    ],
    visibility = [
        &amp;quot;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus:__subpackages__&amp;quot;,
        &amp;quot;&#x2F;&#x2F;javatests&#x2F;fr&#x2F;enoent&#x2F;phosphorus:__subpackages__&amp;quot;,
    ],
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And that&#x27;s it, we have our first library ready to be compiled! I won&#x27;t describe
all the modules as it&#x27;s pretty repetitive and there&#x27;s not a lot of value into
doing that, but let&#x27;s see what the main binary looks like. Defined in
&lt;code&gt;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;BUILD&lt;&#x2F;code&gt;, we have:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;@io_bazel_rules_kotlin&#x2F;&#x2F;kotlin:kotlin.bzl&amp;quot;, &amp;quot;kt_jvm_binary&amp;quot;)

kt_jvm_binary(
    name = &amp;quot;phosphorus&amp;quot;,
    srcs = [
        &amp;quot;Phosphorus.kt&amp;quot;,
    ],
    main_class = &amp;quot;fr.enoent.phosphorus.PhosphorusKt&amp;quot;,
    visibility = [&amp;quot;&#x2F;&#x2F;visibility:public&amp;quot;],
    deps = [
        &amp;quot;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;differ&amp;quot;,
        &amp;quot;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;differ&#x2F;impl:module&amp;quot;,
        &amp;quot;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;loader&amp;quot;,
        &amp;quot;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;loader&#x2F;io_impl:module&amp;quot;,
        &amp;quot;&#x2F;&#x2F;third_party&#x2F;dagger&amp;quot;,
        &amp;quot;@maven&#x2F;&#x2F;:com_github_ajalt_clikt&amp;quot;,
    ],
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note the name of the &lt;code&gt;main_class&lt;&#x2F;code&gt;: because it&#x27;s a Kotlin class, the compiler
will append &lt;code&gt;Kt&lt;&#x2F;code&gt; at the end of its name. Once this is defined, we can run
Phosphorus with this command:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;bazel run &#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus -- arguments passed to Phosphorus directly
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;tests&quot;&gt;Tests&lt;&#x2F;h2&gt;
&lt;p&gt;As mentioned previously, the test root will be &lt;code&gt;&#x2F;&#x2F;javatests&lt;&#x2F;code&gt;. Because we need to
follow the packages structure, the tests themselves will be under
&lt;code&gt;&#x2F;&#x2F;javatests&#x2F;fr&#x2F;enoent&#x2F;phosphorus&lt;&#x2F;code&gt;. They are regular JUnit 4 tests, using Truth
for the assertions.&lt;&#x2F;p&gt;
&lt;p&gt;Defining unit tests is really straightforward, and follows really closely the
pattern we saw with libraries and binaries. For example, the &lt;code&gt;ImageTest&lt;&#x2F;code&gt; test is
defined like this, in &lt;code&gt;&#x2F;&#x2F;javatests&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;data&#x2F;BUILD&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;@io_bazel_rules_kotlin&#x2F;&#x2F;kotlin:kotlin.bzl&amp;quot;, &amp;quot;kt_jvm_test&amp;quot;)

kt_jvm_test(
    name = &amp;quot;ImageTest&amp;quot;,
    srcs = [&amp;quot;ImageTest.kt&amp;quot;],
    deps = [
        &amp;quot;&#x2F;&#x2F;java&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;data&amp;quot;,
        &amp;quot;@maven&#x2F;&#x2F;:com_google_truth_truth&amp;quot;,
        &amp;quot;@maven&#x2F;&#x2F;:junit_junit&amp;quot;,
    ],
)

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will define a Bazel target that we can invoke like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;bazel test &#x2F;&#x2F;javatests&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;data:ImageTest
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Hopefully, the output should look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&#x2F;&#x2F;javatests&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;data:ImageTest                          PASSED in 0.3s
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once this is done, it&#x27;s possible to run
&lt;code&gt;ibazel test &#x2F;&#x2F;javatests&#x2F;fr&#x2F;enoent&#x2F;phosphorus&#x2F;...&lt;&#x2F;code&gt; - Bazel will then monitor all
the test targets defined under that path, as well as their dependencies, and
re-run all the affected tests as soon as something is edited. Because Bazel
encourages small build targets, has some great caching, and the Kotlin compiler
uses a persistent worker, the feedback loop is really quick.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;static-analysis&quot;&gt;Static analysis&lt;&#x2F;h2&gt;
&lt;p&gt;For Kotlin, two tools are quite useful:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;arturbosch.github.io&#x2F;detekt&#x2F;&quot;&gt;Detekt&lt;&#x2F;a&gt;, and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ktlint.github.io&#x2F;&quot;&gt;Ktlint&lt;&#x2F;a&gt;. The idea to run them will be really
similar: having two supporting test targets for each actual Kotlin target,
running Detekt and Ktlint on its sources. In order to do that easily, we&#x27;ll
define some wrappers around the &lt;code&gt;kt_jvm_*&lt;&#x2F;code&gt; set of rules. Those wrappers will be
responsible for generating the two supporting test targets, as well as calling
the original &lt;code&gt;kt_jvm_*&lt;&#x2F;code&gt; rule. The resulting macro will be entirely transparent
to use, the only difference being the &lt;code&gt;load&lt;&#x2F;code&gt; call.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see what those macros could look like. In &lt;code&gt;&#x2F;&#x2F;java&#x2F;rules&#x2F;defs.bzl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(
    &amp;quot;@io_bazel_rules_kotlin&#x2F;&#x2F;kotlin:kotlin.bzl&amp;quot;,
    upstream_kt_jvm_binary = &amp;quot;kt_jvm_binary&amp;quot;,
    upstream_kt_jvm_library = &amp;quot;kt_jvm_library&amp;quot;,
    upstream_kt_jvm_test = &amp;quot;kt_jvm_test&amp;quot;,
)
def kt_jvm_binary(name, srcs, **kwargs):
    upstream_kt_jvm_binary(
        name = name,
        srcs = srcs,
        **kwargs
    )

    _common_tests(name = name, srcs = srcs)

def kt_jvm_library(name, srcs, **kwargs):
    upstream_kt_jvm_library(
        name = name,
        srcs = srcs,
        **kwargs
    )

    _common_tests(name = name, srcs = srcs)

def kt_jvm_test(name, srcs, size = &amp;quot;small&amp;quot;, **kwargs):
    upstream_kt_jvm_test(
        name = name,
        srcs = srcs,
        size = size,
        **kwargs
    )

    _common_tests(name = name, srcs = srcs)

def _common_tests(name, srcs):
    # This will come soon, no-op for now
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With those wrappers defined, we need to actually call them. Because we&#x27;re
following the same signature and name as the upstream rules, we just need to
update our &lt;code&gt;load&lt;&#x2F;code&gt; calls in the different &lt;code&gt;BUILD&lt;&#x2F;code&gt; files.
&lt;code&gt;load(&quot;@io_bazel_rules_kotlin&#x2F;&#x2F;kotlin:kotlin.bzl&quot;, &quot;kt_jvm_test&quot;)&lt;&#x2F;code&gt; will become
&lt;code&gt;load(&quot;&#x2F;&#x2F;java&#x2F;rules:defs.bzl&quot;, &quot;kt_jvm_test&quot;)&lt;&#x2F;code&gt;, and so on. &lt;code&gt;_common_tests&lt;&#x2F;code&gt; will
be responsible for calling Detekt and Ktlint, let&#x27;s see how.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;detekt&quot;&gt;Detekt&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;artem_zin&quot;&gt;Artem Zinnatullin&lt;&#x2F;a&gt; published a
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;buildfoundation&#x2F;bazel_rules_detekt&#x2F;&quot;&gt;set of rules&lt;&#x2F;a&gt; to run
Detekt a week before I started writing this, making things way easier. As usual,
let&#x27;s start by loading this in the &lt;code&gt;WORKSPACE&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;http_file(
    name = &amp;quot;detekt_cli_jar&amp;quot;,
    sha256 = &amp;quot;e9710fb9260c0824b3a9ae7d8326294ab7a01af68cfa510cab66de964da80862&amp;quot;,
    urls = [&amp;quot;https:&#x2F;&#x2F;jcenter.bintray.com&#x2F;io&#x2F;gitlab&#x2F;arturbosch&#x2F;detekt&#x2F;detekt-cli&#x2F;1.2.0&#x2F;detekt-cli-1.2.0-all.jar&amp;quot;],
)

http_archive(
    name = &amp;quot;rules_detekt&amp;quot;,
    sha256 = &amp;quot;f1632c2492291f5144a5e0f5e360a094005e20987518d228709516cc935ad1a1&amp;quot;,
    strip_prefix = &amp;quot;bazel_rules_detekt-0.2.0&amp;quot;,
    url = &amp;quot;https:&#x2F;&#x2F;github.com&#x2F;buildfoundation&#x2F;bazel_rules_detekt&#x2F;archive&#x2F;v0.2.0.zip&amp;quot;,
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This exposes a rule named &lt;code&gt;detekt&lt;&#x2F;code&gt;, which defines a build target, generating the
Detekt report. While there are a few options, we&#x27;ll keep things simple. This is
what a basic invocation looks like, in any &lt;code&gt;BUILD&lt;&#x2F;code&gt; file:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;detekt(
    name = &amp;quot;detekt_report&amp;quot;,
    srcs = glob([&amp;quot;**&#x2F;*.kt&amp;quot;]),
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can integrate that in our &lt;code&gt;_common_tests&lt;&#x2F;code&gt; macro, to generate a Detekt target
automatically for every Kotlin target:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def _common_tests(name, srcs):
    detekt(
        name = &amp;quot;%s_detekt_report&amp;quot; % name,
        srcs = srcs,
        config = &amp;quot;&#x2F;&#x2F;java&#x2F;rules&#x2F;internal:detekt-config.yml&amp;quot;,
    )
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All our Kotlin targets now have a &lt;code&gt;$name_detekt_report&lt;&#x2F;code&gt; target generated
automatically, using a common Detekt configuration.&lt;&#x2F;p&gt;
&lt;p&gt;The way this &lt;code&gt;detekt&lt;&#x2F;code&gt; rule work is by creating a build target, that generates
the report. Which means that it&#x27;s not actually a test - which is what we were
trying to achieve. In order to do this, we can use
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;bazel-skylib&quot;&gt;Bazel Skylib&lt;&#x2F;a&gt;&#x27;s &lt;code&gt;build_test&lt;&#x2F;code&gt;. This
test rule generates a test target that just has a dependency on other targets -
if any of those dependencies fails to build, then the test fails. Otherwise, it
passes. Our macro becomes:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def _common_tests(name, srcs):
    detekt(
        name = &amp;quot;%s_detekt_report&amp;quot; % name,
        srcs = srcs,
        config = &amp;quot;&#x2F;&#x2F;java&#x2F;rules&#x2F;internal:detekt-config.yml&amp;quot;,
    )

    build_test(
        name = &amp;quot;%s_detekt_test&amp;quot; % name,
        targets = [&amp;quot;:%s_detekt_report&amp;quot; % name],
    )
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And there we have it - a &lt;code&gt;$name_detekt_test&lt;&#x2F;code&gt; that is actually a test, and will
fail if Detekt raises errors.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ktlint&quot;&gt;Ktlint&lt;&#x2F;h3&gt;
&lt;p&gt;Ktlint doesn&#x27;t have any existing open-source rules. Let&#x27;s see how we can write
our own minimal one. It will take as inputs the list of files to check, as well
as an optional &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;editorconfig.org&#x2F;&quot;&gt;editorconfig&lt;&#x2F;a&gt; configuration, that
Ktlint supports natively.&lt;&#x2F;p&gt;
&lt;p&gt;The definition of the rules will be split in three files: two internal files
defining respectively the &lt;em&gt;action&lt;&#x2F;em&gt; (how to invoke Ktlint) and the &lt;em&gt;rule
interface&lt;&#x2F;em&gt; (what&#x27;s its name, its arguments...), as well as a third, public file,
meant to be consumed by users.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start by downloading Ktlint itself. In the &lt;code&gt;WORKSPACE&lt;&#x2F;code&gt;, as usual:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;http_file(
    name = &amp;quot;com_github_pinterest_ktlint&amp;quot;,
    executable = True,
    sha256 = &amp;quot;a656342cfce5c1fa14f13353b84b1505581af246638eb970c919fb053e695d5e&amp;quot;,
    urls = [&amp;quot;https:&#x2F;&#x2F;github.com&#x2F;pinterest&#x2F;ktlint&#x2F;releases&#x2F;download&#x2F;0.36.0&#x2F;ktlint&amp;quot;],
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s move onto the action definition. It&#x27;s a simple macro returning a string,
which defines how to invoke Ktlint, given some arguments. In
&lt;code&gt;&#x2F;&#x2F;tools&#x2F;ktlint&#x2F;internal&#x2F;actions.bzl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def ktlint(ctx, srcs, editorconfig):
    &amp;quot;&amp;quot;&amp;quot;Generates a test action linting the input files.

    Args:
      ctx: analysis context.
      srcs: list of source files to be checked.
      editorconfig: editorconfig file to use (optional)

    Returns:
      A script running ktlint on the input files.
    &amp;quot;&amp;quot;&amp;quot;

    args = []

    if editorconfig:
        args.append(&amp;quot;--editorconfig={file}&amp;quot;.format(file = editorconfig.short_path))


    for f in srcs:
        args.append(f.path)

    return &amp;quot;{linter} {args}&amp;quot;.format(
        linter = ctx.executable._ktlint_tool.short_path,
        args = &amp;quot; &amp;quot;.join(args),
    )
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pretty straightforward - we combine both Ktlint&#x27;s executable path, the
editorconfig file if it&#x27;s provided, and the list of source files.&lt;&#x2F;p&gt;
&lt;p&gt;Now for the rule interface, we will define a rule named &lt;code&gt;ktlint_test&lt;&#x2F;code&gt;. Building
a &lt;code&gt;ktlint_test&lt;&#x2F;code&gt; target will mean generating a shell script to invoke Ktlint with
the given set of argument, and running it will invoke that script - hence
running Ktlint as well. In &lt;code&gt;&#x2F;&#x2F;tools&#x2F;ktlint&#x2F;internal&#x2F;rules.bzl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;:actions.bzl&amp;quot;, &amp;quot;ktlint&amp;quot;)

def _ktlint_test_impl(ctx):
    script = ktlint(
        ctx,
        srcs = ctx.files.srcs,
        editorconfig = ctx.file.editorconfig,
    )

    ctx.actions.write(
        output = ctx.outputs.executable,
        content = script,
    )

    files = [ctx.executable._ktlint_tool] + ctx.files.srcs

    if ctx.file.editorconfig:
        files.append(ctx.file.editorconfig)

    return [
        DefaultInfo(
            runfiles = ctx.runfiles(
                files = files,
            ).merge(ctx.attr._ktlint_tool[DefaultInfo].default_runfiles),
            executable = ctx.outputs.executable,
        ),
    ]

ktlint_test = rule(
    _ktlint_test_impl,
    attrs = {
        &amp;quot;srcs&amp;quot;: attr.label_list(
            allow_files = [&amp;quot;.kt&amp;quot;, &amp;quot;.kts&amp;quot;],
            doc = &amp;quot;Source files to lint&amp;quot;,
            mandatory = True,
            allow_empty = False,
        ),
        &amp;quot;editorconfig&amp;quot;: attr.label(
            doc = &amp;quot;Editor config file to use&amp;quot;,
            mandatory = False,
            allow_single_file = True,
        ),
        &amp;quot;_ktlint_tool&amp;quot;: attr.label(
            default = &amp;quot;@com_github_pinterest_ktlint&#x2F;&#x2F;file&amp;quot;,
            executable = True,
            cfg = &amp;quot;target&amp;quot;,
        ),
    },
    doc = &amp;quot;Lint Kotlin files, and fail if the linter raises errors.&amp;quot;,
    test = True,
)

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We have two different parts here - the definition of the interface, with the
call to &lt;code&gt;rule&lt;&#x2F;code&gt;, and the implementation of that rule, defined as
&lt;code&gt;_ktlint_test_impl&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The call to &lt;code&gt;rule&lt;&#x2F;code&gt; define how this rule can be invoked. We define that it
requires a list of &lt;code&gt;.kt&lt;&#x2F;code&gt; and&#x2F;or &lt;code&gt;.kts&lt;&#x2F;code&gt; files named &lt;code&gt;srcs&lt;&#x2F;code&gt;, an optional file
named &lt;code&gt;editorconfig&lt;&#x2F;code&gt;, as well as a hidden argument named &lt;code&gt;_ktlint_tool&lt;&#x2F;code&gt;, which
is just a helper for us to reference the Ktlint binary - to which we pass the
file we defined in the &lt;code&gt;WORKSPACE&lt;&#x2F;code&gt; earlier.&lt;&#x2F;p&gt;
&lt;p&gt;The actual implementation is working in multiple steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It invokes the &lt;code&gt;ktlint&lt;&#x2F;code&gt; action we defined earlier, to generate the script
that will be invoked.&lt;&#x2F;li&gt;
&lt;li&gt;It generates an action to write that script, in a file referred as
&lt;code&gt;ctx.outputs.executable&lt;&#x2F;code&gt; (which Bazel knows how to handle and what to do with
it, we don&#x27;t need to worry about where it is or anything, it won&#x27;t be in the
source tree anyway).&lt;&#x2F;li&gt;
&lt;li&gt;It computes a list of files that are needed to run this target. This is what
allows Bazel to ensure hermeticity - it will know that this rule needs to be
re-run if any of those files are changed. If the target runs in a sandboxed
environment (which is the default on most platforms, as far as I&#x27;m aware), only
those files will be available.&lt;&#x2F;li&gt;
&lt;li&gt;It returns a &lt;code&gt;Provider&lt;&#x2F;code&gt;, responsible for holding a description of what this
target needs.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Finally, we write a file that only exposes the bits users should care about.
It&#x27;s not mandatory, but makes a clear delimitation between what is an
implementation detail and what users can actually rely on. In
&lt;code&gt;&#x2F;&#x2F;tools&#x2F;ktlint&#x2F;defs.bzl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(
    &amp;quot;&#x2F;&#x2F;tools&#x2F;ktlint&#x2F;internal:rules.bzl&amp;quot;,
    _ktlint_test = &amp;quot;ktlint_test&amp;quot;,
)

ktlint_test = _ktlint_test
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We just expose the rule we wrote in &lt;code&gt;rules.bzl&lt;&#x2F;code&gt; as &lt;code&gt;ktlint_test&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Once this is done, we can use this &lt;code&gt;ktlint_test&lt;&#x2F;code&gt; rule where we needed it, in our
&lt;code&gt;_common_tests&lt;&#x2F;code&gt; macro for Kotlin targets:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def _common_tests(name, srcs):
    ktlint_test(
        name = &amp;quot;%s_ktlint_test&amp;quot; % name,
        srcs = srcs,
        editorconfig = &amp;quot;&#x2F;&#x2F;:.editorconfig&amp;quot;,
    )

    detekt(
        name = &amp;quot;%s_detekt_report&amp;quot; % name,
        srcs = srcs,
        config = &amp;quot;&#x2F;&#x2F;java&#x2F;rules&#x2F;internal:detekt-config.yml&amp;quot;,
    )

    build_test(
        name = &amp;quot;%s_detekt_test&amp;quot; % name,
        targets = [&amp;quot;:%s_detekt_report&amp;quot; % name],
    )
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And there we have it - all our Kotlin targets have both Detekt and Ktlint test
targets. Because we&#x27;re exposing those as Bazel targets, we automatically benefit
from its caching and remote execution capabilities - those linters won&#x27;t re-run
if the inputs didn&#x27;t change, and can run remotely, with Bazel being aware of
which files are needed on the remote machine.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;But what&#x27;s the link between generating a blog with Bazel and compiling a Kotlin
application? Well, almost none, but there is one. The class diagram included
earlier in this article is generated with a tool called
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;plantuml.com&#x2F;&quot;&gt;PlantUML&lt;&#x2F;a&gt;, which generates images from a text
representation of a graph. The next article in this series will talk about
integrating this tool into Bazel (in a similar way as we did with Ktlint), but
also how to test the Bazel rule. And to have some integration tests, Phosphorus
will come in handy!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Why Bazel?</title>
        <published>2019-11-02T18:00:00+11:00</published>
        <updated>2019-11-02T18:00:00+11:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/creating-a-blog-with-bazel/03-why-bazel/"/>
        <id>https://enoent.fr/posts/creating-a-blog-with-bazel/03-why-bazel/</id>
        
        <content type="html" xml:base="https://enoent.fr/posts/creating-a-blog-with-bazel/03-why-bazel/">&lt;p&gt;In this post, we&#x27;ll cover what &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bazel.build&quot;&gt;Bazel&lt;&#x2F;a&gt; is, how to use it,
and why I chose to use it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-bazel&quot;&gt;What is Bazel?&lt;&#x2F;h2&gt;
&lt;p&gt;Bazel is a build-system released by Google in 2015. It actually is derived from
the internal build-system Google uses internally for most of its own code-base,
called
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mike-bland.com&#x2F;2012&#x2F;10&#x2F;01&#x2F;tools.html#blaze-forge-srcfs-objfs&quot;&gt;Blaze&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;building-at-scale&quot;&gt;Building at scale&lt;&#x2F;h3&gt;
&lt;p&gt;Bazel has a huge focus on hermetic builds, and reproducibility. Every build step
is, from a really broad perspective, defined as a list of inputs, tools, and
outputs. This allows for efficient and robust caching (if no inputs nor tools
changed, then this target doesn&#x27;t need to be rebuilt, and this cascades through
the whole build graph). Let&#x27;s see a sample definition of a C++ library, as well
as a C++ binary depending on it:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;BUILD&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;cc_library(
    name = &amp;quot;my_feature&amp;quot;
    srcs = [
        &amp;quot;feature_impl.cpp&amp;quot;,
        &amp;quot;utils.cpp&amp;quot;,
    ],
    hdrs = [
        &amp;quot;feature.hpp&amp;quot;,
        &amp;quot;utils.hpp&amp;quot;,
    ],
)

cc_binary(
    name = &amp;quot;my_app&amp;quot;,
    srcs = [&amp;quot;main.cpp&amp;quot;],
    deps = [
        &amp;quot;:my_feature&amp;quot;,
    ],
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;cc_library&lt;&#x2F;code&gt; and &lt;code&gt;cc_binary&lt;&#x2F;code&gt; are both depending an implicit dependency on a C++
toolchain (I won&#x27;t enter into any language-specific features in this post, but
if you don&#x27;t tell Bazel to use a specific C++ toolchain, it will try to use your
system compiler - which is convenient, but loses a bit of hermeticity and
reproducibility). Everything else is pretty obvious here: we defined two
different build targets, one of them being a library called &lt;code&gt;my_feature&lt;&#x2F;code&gt;, and
the other one a binary called &lt;code&gt;my_app&lt;&#x2F;code&gt;, depending on &lt;code&gt;my_feature&lt;&#x2F;code&gt;. If we build
&lt;code&gt;my_app&lt;&#x2F;code&gt;, Bazel will automatically build &lt;code&gt;my_feature&lt;&#x2F;code&gt; first as you would expect,
and then proceed to build &lt;code&gt;my_app&lt;&#x2F;code&gt;. If you change the &lt;code&gt;main.cpp&lt;&#x2F;code&gt; and re-build
&lt;code&gt;my_app&lt;&#x2F;code&gt;, it will skip the compilation of &lt;code&gt;my_feature&lt;&#x2F;code&gt; entirely, as nothing
changed.&lt;&#x2F;p&gt;
&lt;p&gt;Bazel&#x27;s cache handling is really reliable. During the past few months, I&#x27;ve done
a lot of diverse things (writing my own rules, compiling a bunch of different
languages, depending on third-party libraries and rules...), and never had a
single time to run &lt;code&gt;bazel clean&lt;&#x2F;code&gt;. Now I didn&#x27;t use a lot of other build systems
in the recent past, but from someone who has been using
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gradle.org&#x2F;&quot;&gt;Gradle&lt;&#x2F;a&gt; for Android previously, this feels really weird.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;integrating-tools-and-other-languages&quot;&gt;Integrating tools and other languages&lt;&#x2F;h3&gt;
&lt;p&gt;Another great aspect of Bazel is its extensibility. It works with rules defined
in a language called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;starlark&quot;&gt;Starlark&lt;&#x2F;a&gt;, which
syntax is a subset of Python&#x27;s. It comes without a lot of standard Python
features, as I&#x2F;O, mutable collections, or anything that could affect build
hermeticity. While this isn&#x27;t the focus of this article (I will cover the
writing of a rule to run a simple tool in a later article), here is what an
example rule can look like (from
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;examples&#x2F;blob&#x2F;master&#x2F;rules&#x2F;shell_command&#x2F;rules.bzl&quot;&gt;Bazel&#x27;s samples&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;rules.bzl&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;def _convert_to_uppercase_impl(ctx):
    # Both the input and output files are specified by the BUILD file.
    in_file = ctx.file.input
    out_file = ctx.outputs.output
    ctx.actions.run_shell(
        outputs = [out_file],
        inputs = [in_file],
        arguments = [in_file.path, out_file.path],
        command = &amp;quot;tr &amp;#39;[:lower:]&amp;#39; &amp;#39;[:upper:]&amp;#39; &amp;lt; \&amp;quot;$1\&amp;quot; &amp;gt; \&amp;quot;$2\&amp;quot;&amp;quot;,
    )
    # No need to return anything telling Bazel to build `out_file` when
    # building this target -- It&amp;#39;s implied because the output is declared
    # as an attribute rather than with `declare_file()`.

convert_to_uppercase = rule(
    implementation = _convert_to_uppercase_impl,
    attrs = {
        &amp;quot;input&amp;quot;: attr.label(
            allow_single_file = True,
            mandatory = True,
            doc = &amp;quot;The file to transform&amp;quot;,
        ),
        &amp;quot;output&amp;quot;: attr.output(doc = &amp;quot;The generated file&amp;quot;),
    },
    doc = &amp;quot;Transforms a text file by changing its characters to uppercase.&amp;quot;,
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Once it&#x27;s defined, it&#x27;s re-usable to define actual build targets in a simple way:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;BUILD&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;load(&amp;quot;:rules.bzl&amp;quot;, &amp;quot;convert_to_uppercase&amp;quot;)

convert_to_uppercase(
    name = &amp;quot;foo_but_uppercase&amp;quot;,
    input = &amp;quot;foo.txt&amp;quot;,
    output = &amp;quot;upper_foo.txt&amp;quot;,
)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As a result of this simple extensibility, while Bazel ships only with C++ and
Java support (which are actually getting removed and rewritten in Starlark, to
decouple them from Bazel itself), a lot of rules have been written either by the
Bazel team or by the community, to integrate languages and tools. You can find
rules for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_nodejs&quot;&gt;NodeJS&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_go&quot;&gt;Go&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_rust&quot;&gt;Rust&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_pkg&quot;&gt;packaging&lt;&#x2F;a&gt; (generating debs, zips...),
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_docker&quot;&gt;generating Docker images&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;rules_k8s&quot;&gt;deploying stuff on Kubernetes&lt;&#x2F;a&gt;, and a
bunch of other things. And if there are no rules to run&#x2F;build what you want, you
can write your own!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-three-steps-build&quot;&gt;A three-steps build&lt;&#x2F;h3&gt;
&lt;p&gt;Bazel runs in
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.bazel.build&#x2F;versions&#x2F;master&#x2F;guide.html#phases&quot;&gt;three distinct phases&lt;&#x2F;a&gt;.
Each of them has a specific role, and specific capabilities.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;loading&quot;&gt;Loading&lt;&#x2F;h4&gt;
&lt;p&gt;The loading phase is parsing and evaluating all the &lt;code&gt;BUILD&lt;&#x2F;code&gt; files required to
build the requested target(s). This is typically the step during witch any
third-party dependency would be fetched (just downloaded and&#x2F;or extracted,
nothing more yet).&lt;&#x2F;p&gt;
&lt;h4 id=&quot;analysis&quot;&gt;Analysis&lt;&#x2F;h4&gt;
&lt;p&gt;The second phase is validating any involved build rule, to generate the actual
build graph. Note that both of those two first phases are entirely cached, and
if the build graph doesn&#x27;t change from one build to another (e.g. you just
changed some source files), they will be skipped entirely.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;execution&quot;&gt;Execution&lt;&#x2F;h4&gt;
&lt;p&gt;This is the phase that checks for any out-of-date output (either non-existent,
or its inputs changed), and runs the matching actions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;great-tooling&quot;&gt;Great tooling&lt;&#x2F;h3&gt;
&lt;p&gt;Bazel comes with some really cool tools. Without spending too much time on that,
here&#x27;s a list of useful things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;bazel-watcher&quot;&gt;ibazel&lt;&#x2F;a&gt; is a filesystem-watcher
that will rebuild a target as soon as its inputs files or dependencies
changed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.bazel.build&#x2F;versions&#x2F;master&#x2F;query-how-to.html&quot;&gt;query&lt;&#x2F;a&gt; is a
built-in sub-command that helps to analyse the build graph. It&#x27;s incredibly
feature-packed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;buildtools&#x2F;tree&#x2F;master&#x2F;buildozer&quot;&gt;buildozer&lt;&#x2F;a&gt; is
a tool to edit &lt;code&gt;BUILD&lt;&#x2F;code&gt; files at across a whole repository. It can be used to
add dependencies to specific targets, changing target visibilities, adding
comments...&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;buildtools&#x2F;blob&#x2F;master&#x2F;unused_deps&#x2F;README.md&quot;&gt;unused_deps&lt;&#x2F;a&gt;
is detecting unused dependencies for Java targets, and displays &lt;code&gt;buildozer&lt;&#x2F;code&gt;
commands to remove them.&lt;&#x2F;li&gt;
&lt;li&gt;Integration &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;intellij&quot;&gt;with&lt;&#x2F;a&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;vscode-bazel&quot;&gt;different&lt;&#x2F;a&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;vim-bazel&quot;&gt;IDEs&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;A set of APIs for remote caching and execution, with
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;BuildGrid&#x2F;buildgrid&quot;&gt;a&lt;&#x2F;a&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;bazel-buildfarm&quot;&gt;few&lt;&#x2F;a&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;buildbarn&quot;&gt;implementations&lt;&#x2F;a&gt;, as well as an upcoming
service on Google Cloud called Remote Build Execution, leveraging GCP to build
remotely. The loading and analysis phases are still running locally, while the
execution phase is running remotely.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;choosing-a-build-system&quot;&gt;Choosing a build system&lt;&#x2F;h2&gt;
&lt;p&gt;At the time I started thinking about working on this blog again, I had a small
private repository with a bunch of stuff, all compiled with Bazel. I also
noticed a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;stackb&#x2F;rules_hugo&quot;&gt;set of Starlark rules&lt;&#x2F;a&gt;
integrating &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;. While I didn&#x27;t need a build system,
Bazel seemed to be interesting for multiple aspects:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I could leverage my existing CI system&lt;&#x2F;li&gt;
&lt;li&gt;While Hugo comes with a bunch of features to e.g. pre-process Sass files, it
has some kind of lock-in effect. What if I eventually realise that Hugo
doesn&#x27;t fill my need? What&#x27;s the cost of migrating to a new static site
generator? The less I rely on Hugo-specific features, the easier this would be&lt;&#x2F;li&gt;
&lt;li&gt;I could integrate some custom asset pipelines. For example, I could have a
diagram written with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;plantuml.com&#x2F;&quot;&gt;PlantUML&lt;&#x2F;a&gt; or
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mermaidjs.github.io&#x2F;&quot;&gt;Mermaid&lt;&#x2F;a&gt; and have it part of the Bazel graph,
as a dependency of this blog&lt;&#x2F;li&gt;
&lt;li&gt;Bazel would be able to handle packaging and deployment&lt;&#x2F;li&gt;
&lt;li&gt;It sounded stupid enough to be a fun experiment? (Let&#x27;s be honest, that&#x27;s the
only real reason here.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;Bazel is quite complex, and this article only scratches the surface. The goal
was not to teach you how to use Bazel (there are a lot of existing resources for
that already), but to give a quick overview of the core ideas behind it.&lt;&#x2F;p&gt;
&lt;p&gt;If you found it interesting, here are some useful links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Bazel&#x27;s
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.bazel.build&#x2F;versions&#x2F;master&#x2F;getting-started.html&quot;&gt;getting started&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bazelbuild&#x2F;examples&quot;&gt;list of samples&lt;&#x2F;a&gt; using different
languages as well as defining some rules&lt;&#x2F;li&gt;
&lt;li&gt;A (non-exhaustive)
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.bazel.build&#x2F;versions&#x2F;master&#x2F;rules.html&quot;&gt;list of rules&lt;&#x2F;a&gt;, as well
as the documentation of all the built-in rules&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In the next article, we&#x27;ll see how to build a simple Kotlin app with Bazel, from
scratch all the way to running it.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A new beginning</title>
        <published>2019-10-31T21:05:00+11:00</published>
        <updated>2019-10-31T21:05:00+11:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/creating-a-blog-with-bazel/01-a-new-beginning/"/>
        <id>https://enoent.fr/posts/creating-a-blog-with-bazel/01-a-new-beginning/</id>
        
        <content type="html" xml:base="https://enoent.fr/posts/creating-a-blog-with-bazel/01-a-new-beginning/">&lt;p&gt;This blog has been inactive for a long time. I tried to at least post an article
yearly, and next thing you know, two years and a half fly by... Halloween seemed
like a good time to resurrect it.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to start writing again recently, and faced an issue: this blog was
using Octopress 2. Well, Octopress has &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;octopress.org&#x2F;&quot;&gt;apparently been dead for even longer
than this blog&lt;&#x2F;a&gt;. So I wanted to switch to another static
generator. I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gohugo.io&#x2F;&quot;&gt;Hugo&lt;&#x2F;a&gt;, which is actively maintained and
ticked all the boxes I had, so that&#x27;s what I settled for (sorry for the probable
RSS feed mess - while I set up 301 redirects for the old articles, I guess this
won&#x27;t play nicely with any RSS reader. This is actually what prompted this
article...)&lt;&#x2F;p&gt;
&lt;p&gt;This could have been an hour worth of work - migrating the content (both Hugo
and Octopress are using Markdown, so that part was really simple), finding or
putting together a nice template, and call it a day. But how fun is that?
Instead, I chose to go with the most complex (hence fun, right?) approach
possible. And that was by using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bazel.build&#x2F;&quot;&gt;Bazel&lt;&#x2F;a&gt; to do
&lt;em&gt;everything&lt;&#x2F;em&gt;. Sass linting and pre-processing, HTML generation, generating a
Docker image, deploying it... and with tests for a lot of things along the way.
Today, the deployment part is still missing (I&#x27;m working on it), but everything
else is pretty much ready.&lt;&#x2F;p&gt;
&lt;p&gt;I plan to describe this whole journey soon, although I don&#x27;t know exactly which
form it will take yet - probably a series of small articles covering a specific
aspect. In the meantime, welcome back on a brand-new blog!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Compat libraries incompatibilities</title>
        <published>2017-05-06T19:52:26+02:00</published>
        <updated>2017-05-06T19:52:26+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/compat-libraries-incompatibilities/"/>
        <id>https://enoent.fr/posts/compat-libraries-incompatibilities/</id>
        
        <summary type="html">&lt;p&gt;Compat libraries are great. They allow us to work with the newest Android APIs,
without thinking (much) about your minimum API level. Instead of thousands
of devices, you can reach billions. With nearly no changes in your code.&lt;&#x2F;p&gt;
&lt;p&gt;But sometimes, they&#x27;re not so great…&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Android Things: first look</title>
        <published>2017-01-06T10:55:08+01:00</published>
        <updated>2017-01-06T10:55:08+01:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/android-things-first-look/"/>
        <id>https://enoent.fr/posts/android-things-first-look/</id>
        
        <summary type="html">&lt;h2 id=&quot;what-is-android-things&quot;&gt;What is Android Things?&lt;&#x2F;h2&gt;
&lt;p&gt;Android Things is an alternative Android version, announced at Google I&#x2F;O 2015,
and released as a first developer preview in December 2016. Its purpose is to
develop embedded IoT devices, with a known and widely documented Android
ecosystem basis.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s currently running on three different boards: the Intel Edison, the NXP Pico
i.MX6UL, and the Raspberry Pi 3. Some higher-end boards are coming soon.&lt;&#x2F;p&gt;
&lt;p&gt;On the SDK side, Android Things comes with a specific support library to ease
low-level hardware usage. It consists in two parts: the Peripheral I&#x2F;O API,
which supports GPIO, PWM, I2C, SPI and UART, and the User Driver API, which
allows a developer to write a hardware-specific, high-level driver, to ease
hardware reusability by injecting events into the Android framework. Other
applications can in turn use those events without having to interact with the
hardware directly.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a downside: the bundled Android is not as complete as the one you can
find on a phone. Most of the standard applications aren&#x27;t installed (Calendar,
Phone…), and standard content providers are absent too (MediaProvider,
Dictionary…).&lt;&#x2F;p&gt;
&lt;p&gt;Android Things supports displays, with the default Android UI toolkit. However,
the display is a bit different from what you&#x27;re used to seeing on an Android
device: there&#x27;s no notification bar, navigation bar or anything, the running
application will use the full display. That is, if it uses it at all: displays
are purely optional.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Use Apache HTTP Client on Android SDK 23</title>
        <published>2015-10-01T15:46:00+02:00</published>
        <updated>2015-10-01T15:46:00+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/use-apache-http-client-on-android-sdk-23/"/>
        <id>https://enoent.fr/posts/use-apache-http-client-on-android-sdk-23/</id>
        
        <summary type="html">&lt;p&gt;With the Android M SDK (API 23), Google removed the Apache HTTP Client library.
It was deprecated since API 22, and Google recommanded to use
&lt;code&gt;HttpURLConnection&lt;&#x2F;code&gt; instead since API 9. While the classes are still bundled in
Android 6 ROMs, it won&#x27;t be long until we see them completely go away.&lt;&#x2F;p&gt;
&lt;p&gt;Some applications are still relying on this library, and need to be updated to
use the SDK 23, without having time&#x2F;budget&#x2F;whatever required to switch from
HTTP Client. While I strongly recommend you to still take time to move to
something else (there are many high-level libraries, like
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;square.github.io&#x2F;okhttp&#x2F;&quot;&gt;OkHttp&lt;&#x2F;a&gt; or
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;koush&#x2F;ion&quot;&gt;Ion&lt;&#x2F;a&gt;, or you can use &lt;code&gt;HttpURLConnection&lt;&#x2F;code&gt;
to keep a low-level access), there is a way to use the Apache library
while using the SDK 23.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Share code across multiple platforms</title>
        <published>2015-06-11T15:33:00+02:00</published>
        <updated>2015-06-11T15:33:00+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/share-code-across-multiple-platforms/"/>
        <id>https://enoent.fr/posts/share-code-across-multiple-platforms/</id>
        
        <summary type="html">&lt;p&gt;When writing an application, you probably want it to run on most platforms
possible. Having a game on Android is great, but what about this weird friend
with his iPhone? It would be nice to be able to play with him. Of course there
are cross-platforms technologies like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;cordova.apache.org&#x2F;&quot;&gt;Cordova&lt;&#x2F;a&gt; or
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.appcelerator.com&#x2F;titanium&#x2F;&quot;&gt;Titanium&lt;&#x2F;a&gt;. But sadly, you can&#x27;t achieve
both a perfect user experience and great performances with this kind of tools.
And even if you could: what about reusing code on the back-end? We need to share
some code.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>RecyclerView basics</title>
        <published>2015-01-18T19:37:00+01:00</published>
        <updated>2015-01-18T19:37:00+01:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/recyclerview-basics/"/>
        <id>https://enoent.fr/posts/recyclerview-basics/</id>
        
        <summary type="html">&lt;h2 id=&quot;introduction-to-recyclerview&quot;&gt;Introduction to RecyclerView&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;RecyclerView&lt;&#x2F;code&gt; has been introduced with Android 5, in the support-v7 package. It
allows to display a collection of items in an arbitrary disposition (think of a
&lt;code&gt;ListView&lt;&#x2F;code&gt;, but much more flexible). As the name of the support package
indicates, it&#x27;s available from the API level 7 (Android 2.1).&lt;&#x2F;p&gt;
&lt;p&gt;Its name comes from the way it works: when an item is hidden, instead of being
destroyed and a new one being created for each newly displayed item, hidden ones
are &lt;em&gt;recycled&lt;&#x2F;em&gt;: they are reused, with new data bound on them.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Hosting different kinds of apps on nginx</title>
        <published>2014-10-15T10:55:00+02:00</published>
        <updated>2014-10-15T10:55:00+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/hosting-different-kinds-of-apps-on-nginx/"/>
        <id>https://enoent.fr/posts/hosting-different-kinds-of-apps-on-nginx/</id>
        
        <summary type="html">&lt;h2 id=&quot;engine-what&quot;&gt;Engine what?&lt;&#x2F;h2&gt;
&lt;p&gt;Nginx (engine-x) is a web server and reverse proxy for web and mail protocols
(HTTP, HTTPS, SMTP, POP3 and IMAP). It has been first released in 2004, and its
usage keeps growing ever since (according to
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;news.netcraft.com&#x2F;archives&#x2F;2014&#x2F;08&#x2F;27&#x2F;august-2014-web-server-survey.html&quot;&gt;Netcraft&lt;&#x2F;a&gt;,
it was hosting 14.47% of active sites in August 2014).&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s capable of hosting many kinds of applications:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;static HTML pages&lt;&#x2F;li&gt;
&lt;li&gt;PHP, using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;PHP#PHPFPM&quot;&gt;PHP-FPM&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Ruby on Rails and any kind of Rack-based Ruby application, using
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.phusionpassenger.com&#x2F;&quot;&gt;Phusion Passenger&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;proxying requests to another webserver (e.g. a software launching its own web
server, like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;xbmc.org&#x2F;&quot;&gt;Kodi&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Using an API - Trakt.tv example</title>
        <published>2014-10-09T00:06:00+02:00</published>
        <updated>2014-10-09T00:06:00+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/using-an-api-trakt-dot-tv-example/"/>
        <id>https://enoent.fr/posts/using-an-api-trakt-dot-tv-example/</id>
        
        <summary type="html">&lt;p&gt;A lot of websites are generating data which could be really useful outside a
web browser. Having the weather shown on your smartphone&#x27;s lock-screen, the
delay until your next bus… How can we use this from an application?&lt;&#x2F;p&gt;
&lt;p&gt;This article will explain what&#x27;s behind these hidden data flows, and how to
use them. For this purpose, I&#x27;ll use &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;trakt.tv&quot;&gt;Trakt.tv&lt;&#x2F;a&gt; as an example.
If you don&#x27;t know it: Trakt allows you to manage your movie&#x2F;TV series library,
keep track of watch progress, your ratings, comments… and see those of other
people.&lt;&#x2F;p&gt;
&lt;p&gt;Some code will show how to send such requests. It will be written in
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ruby-lang.org&#x2F;&quot;&gt;Ruby&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Compile FFmpeg for Android</title>
        <published>2014-06-20T10:40:00+02:00</published>
        <updated>2014-06-20T10:40:00+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/compile-ffmpeg-for-android/"/>
        <id>https://enoent.fr/posts/compile-ffmpeg-for-android/</id>
        
        <summary type="html">&lt;p&gt;When you have to manipulate audio or video on Android, being used to open-source
software, you have a single name which comes directly to you: FFmpeg. However,
FFmpeg is a C software, meant to be used as an executable, and not officially
supporting Android.&lt;&#x2F;p&gt;
&lt;p&gt;There are a lot of partial and&#x2F;or out-of-date how-to out there on how to get
FFmpeg running on Android, like
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;halfninja&#x2F;android-ffmpeg-x264&quot;&gt;halfninja&#x27;s build&lt;&#x2F;a&gt;. However,
I needed to use FFmpeg &lt;code&gt;concat&lt;&#x2F;code&gt; demuxer, introduced in FFmpeg 1.1. Most builds
target 0.9. There&#x27;s
&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;search?q=ffmpeg+android&quot;&gt;a ton&lt;&#x2F;a&gt;
of questions on StackOverflow about getting newer
FFmpeg releases working on Android. So, here&#x27;s a full explanation to get
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.ffmpeg.org&#x2F;releases&#x2F;ffmpeg-2.2.3.tar.bz2&quot;&gt;FFmpeg 2.2.3 &quot;Muybridge&quot;&lt;&#x2F;a&gt;
working on Android. I&#x27;ll describe the steps for Linux, but everything is pretty
standard shell and should work on any decent OS.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Arduino Leonardo fully-featured keyboard</title>
        <published>2014-05-04T23:03:16+02:00</published>
        <updated>2014-05-04T23:03:16+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/arduino-leonardo-fully-featured-keyboard/"/>
        <id>https://enoent.fr/posts/arduino-leonardo-fully-featured-keyboard/</id>
        
        <summary type="html">&lt;p&gt;The Leonardo has a simple &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;arduino.cc&#x2F;en&#x2F;Reference&#x2F;MouseKeyboard&quot;&gt;keyboard API&lt;&#x2F;a&gt;.
I needed a way to emulate a keyboard (from a joystick and arcade buttons - you
see where I&#x27;m going now). Here&#x27;s how I did it.&lt;&#x2F;p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Android: display a Dialog from an AppWidget</title>
        <published>2014-04-06T22:27:19+02:00</published>
        <updated>2014-04-06T22:27:19+02:00</updated>
        
        <author>
          <name>
            
              Marc Plano-Lesay
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://enoent.fr/posts/android-display-a-dialog-from-an-appwidget/"/>
        <id>https://enoent.fr/posts/android-display-a-dialog-from-an-appwidget/</id>
        
        <summary type="html">&lt;h2 id=&quot;issue&quot;&gt;Issue&lt;&#x2F;h2&gt;
&lt;p&gt;When you want to display a dialog, you don&#x27;t only need a context, you need an
activity context. From an activity, displaying a dialog is pretty
straightforward:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Display a dialog from an activity&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code data-lang=&quot;java&quot;&gt;new AlertDialog.Builder(MyActivity.this)
  .setTitle(&amp;quot;Dialog title&amp;quot;)
  .setMessage(&amp;quot;Dialog message&amp;quot;)
  .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
      &#x2F;&#x2F; Handle a positive answer
    }
  })
  .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
      &#x2F;&#x2F; Handle a negative answer
    }
  })
  .setIcon(R.drawable.ic_dialog_alert)
  .show();
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Okay, that&#x27;s a pretty usual code sample. But what about displaying it from an
app-widget?&lt;&#x2F;p&gt;</summary>
        
    </entry>
</feed>
