Blog

There Is No Such Thing As A Short Term Hack

One day during a review of some changes, a new user interaction was introduced that was basically making use of a toggle in the place of a checkbox. This gave rise to the desire to make other parts of the system have one user control for this behavior. It was said that the change in one spot to use a toggle, with other spots still using the old antiquated checkbox, would confuse users. We should instead take time to understand how far the change would need to extend.

Fast forward a couple of months, one early morning I suggested to a colleague that we create an email group for our team. He said "yeah, but then you have to keep up with it when people come and go, and it becomes a big pain and get's out of date and kind of a hassle" to which I replied something to the effect of "welcome to my world and what it's like to maintain software".


A parallel to both of these is when we collectively as a team say, "Yeah, let's go with the hack for now so we can release...and then later we'll come back and clean it up." What's the commonality? Where is the parallel? All three are a point in time where a decision has to be made about maintenance in one form or another.


What does a hack look like? It comes in many forms. Oddly placed "if(...)" checks sprinkled around. Ever growing indentation levels in a method, indicating harder and harder to understand logic to get you down into those inner most levels. Duplicated identical switch statements hiding classes that are waiting to be born. Code smells sprinkled here and there, and... and... and everywhere.

This business decision of a "short term hack" leaves a bunch of garbage laying around in the code. As a team, we may have great intentions of "coming back to clean it up". But the reality is, that rarely happens... and when it does, it can be the exceptional case. Look, I get it. There is often not any perceived value of coming back to take care of it. But I as a user of that code, have to deal with that. And so do the poor schlubs that I work with that bump into that hack and are left scratching their heads.

It's confusing on several orders of magnitude larger than someone having to handle grocking a checkbox vs. a toggle, or to maintain an email list. Over time, more and more of these "compromises" cause the code to become more and more brittle. It becomes harder and harder to change. Harder and harder to understand. To reason about. The compromise is on the side of the code. The health of the artifact that should be an enabler of speed... of our ability to pivot late in the release cycle... rather than a drag on our speed, becomes more and more un-healthy.


When things begin to take longer and longer to deliver, do we ever look at one another as a team and say "well, this is because we have made some "compromising" decisions along the way that have affected the code adversely and now we're feeling it". I think that's often not the case. Sometimes it is, don't get me wrong. Sometimes we do recall those compromises. And sometimes we take the time to inject some health back into our code artifact.

But, more often is the case that we have lost all context from those far off historical decisions, and now we just feel the pain. Whether it be from too much time passing, or because people that came to the initial "compromise for a short term hack" are no longer around working on the code / on the project / with the company. Or maybe they are still around, but they are not the one staring that compromise in the face at the moment, you are...you're the poor schlub I mentioned above. And there is no way for you to recapture that intimate moment of the past where yes we were able to deliver back then... but now... now you have a story on your Kanban board that's taking forever to make it out of the "Developing" column. So the "compromise" has come back and is staring the larger company straight in the face, specifically you... your product owner... your team. And there you stand as a developer with a cracked egg all over your face. Given enough complaining room and frustration and you might just start to sound like a crotchety wind bag full of hot air about how difficult the code base is. Product owners don't care to hear about the difficulties of the code... they would much rather hear that your story is complete and your ready to take on more work.

Being honest about what it takes to keep a code base healthy is not an easy thing. It's soo necessary though. Be a professional. Do the right thing by the code, and keep lines of communication open between you and your team about what your doing and how it's coming. No, you can't fix the whole system in the course of one story. But yes, you can chew on a mouthful of it. Always making small improvements, and biting off some larger ones along the way can breath new life back into the code asset as well as your team.


Fight like hell to keep them out. Don't offer them up as an option. But (and I do mean but), if you do have add in a "short term hack" in order to deliver (let's all be honest here... it happens... delivery has to happen, it's what keeps the lights on), insist at the very least that a story is created to come back and address the hack. Leave some artifact as a conversation piece to come back to. And make sure that story has plenty of detail around it, capturing as much of the moment as possible to get you back into the context of when the "compromise for the hack" was made. And then fight like hell to play that story as soon as possible.

Short term hacks become long term hacks that harm code agility. Even more, they become longer term problems as you design more and more of your system around them... making additional compromises... predicated on the far off and often forgotten past compromises that the company as a whole made. Keep the code agile, and your company will be too.
Posted in Blogroll

Integrating Stack Exchange and JIRA

Introduction

We are working to get the Red Hat Mobile Application Platform (RHMAP for brevity) Open-Sourced as FeedHenry and have discussed the usual array of community support options : mailing-lists, IRC, GitHub, JIRA, etc. However, the community is already using Stack Overflow to ask questions and get help from one another.

This leads to a question though : how do we integrate Stack Overflow monitoring with our current workflows? Enter so-monitor, a simple node.js application to watch Stack Overflow for questions and track their status in a JIRA. The project creates a ticket when a question is asked using a tag we monitor and then will close the JIRA ticket when the question is marked as answered.

Setup of Development Environment

I decided to write the monitor as a Node.js application, and that I would use Visual Studio Code as my IDE. Being a Java developer I am used to many tooling features: intellisense, robust debugging, etc. Also being a Java developer I had heard that most of these things were just not as mature in the Node.js ecosystem. While I feel this is still true, VS Code and [Definitely Typed|http://definitelytyped.org/] have closed the gap significantly. If you are interested in learning more about this setup I suggest following MS’s blog post. It was very helpful to me.

Consuming the Stack Exchange API

The Stack Exchange API is pretty standard as far as RESTful APIs go. There is a public API that with a limited quota expands more once you get an API key. The biggest difficulty I had was with their filters concept. Basically in Stack Exchange sites you can create a filter for your API calls that lives on their servers and then reference it with a stable, public ID.

I used Moveo’s Stack Exchange API wrapper to access the API and it worked rather well.

JIRA’s black magic

For as simple as Stack Overflow was, JIRA was not. However, JIRA is 100% scriptable and you can configure it to respond to nearly any requirement you may have. Putting a question into JIRA was basically four steps

  • Create the ticket
  • Formatting the question as a issue
  • Tagging my team
  • Closing the issue when the question was answered.

To wrap the JIRA REST API I used steve’s node jira library.

The trickiest part of the process was keeping the issue and the question states in sync. I used a custom field to track the question id returned by the Stack Exchange APIs, but managing the state hit an unexpected snag.

JIRA uses a transitions metaphor for changing the state of a ticket. This means there isn’t a “close issue” API call, but instead you have to upload a “transition” object to the issue endpoint that defines which transition you wish the issue to have taken. This means that you have to either a) check the state of the JIRA issue, lookup a transition, and execute that one or b) create a “Any State” -> “Close” transition in JIRA and hard code that. I chose “b”. For more information I encourage you to read JIRA’s API docs. They are really good and it is a very different pattern than Stack Exchange uses.

Conclusion

While the Stack Exchange and JIRA APIs were the main meat of the monitor project, there were many small “learnings” I had above and beyond that. VS Code and typings is a wonderful JavaScript experience. Using Q promises and wrapping node calls in Promises with simple one liners made my main code much easier to follow. Node.js and the JavaScript ecosystem has come phenomenally far in four years and it is finally as productive as more “Enterprisey” technologies like .Net and Java.

Posted in Blogroll

JRebel and WildFly Swarm in NetBeans

I’m not a great company man; I use NetBeans instead of RedHat’s JBDS. However, I am at least a GOOD company man because I am using WildFly Swarm instead of Sprint Boot. I also use JRebel because I don’t like wasting my time waiting on my projects to build. The combination of the three, however, gave me quite a headache today.

First, NetBeans is the only IDE that gets Maven correct. A Maven project is almost always a valid NetBeans project without any extra configuration. On top of this NetBeans makes it very easy to configure custom Maven goals and map them to your standard IDE actions. Second, WildFly Swarm is an awesome project that is basically Spring Boot but with real, rightsized Java EE. It also has awesome Maven support and I can’t say enough good things about how it. Finally, JRebel should need no introduction. It hot deploys Java code to just about anything and makes the write -> run -> debug cycle as fast as it can be.

My problems began when I imported my Swarm project into NetBeans. NetBeans recognized the fact the project was a war file and offered to deploy it to my installed WildFly server instead of running the Swarm plugin’s run goal. I created a custom NetBeans action to run the Swarm goal. This worked perfectly and all that was missing was JRebel.

JRebel did not want to play nice with the Swarm run goal. I’m not sure why, but eventually I decided to give running the project using the maven exec goal and passing in the Swarm Main class. This worked, but NetBeans wasn’t loading JRebel right out of the box. Finally, I copied over the exec arguments from a working Jar project into my custom run goal and JRebel successfully started (with errors that don’t seem to matter). Hot deploy worked!

If you are wondering here is my configuration :

Execute Goals : process-classes org.codehaus.mojo:exec-maven-plugin:1.2.1:exec
Set Properties : exec.args=-Dexec.args=-Drebel.env.ide.plugin.version=6.5.1 -Drebel.env.ide.version=8.2 -Drebel.env.ide.product=netbeans -Drebel.env.ide=netbeans -Drebel.base=/home/summers/.jrebel -Drebel.notification.url=http://localhost:17434 -agentpath:/home/summers/netbeans-8.2/java2/griffin/lib/libjrebel64.so -classpath %classpath org.wildfly.swarm.Swarm
exec.executable=java

So with that all working my Project Properties looks like this :

screenshot-from-2016-10-14-23-06-42

Posted in Blogroll

Implementing Chip-8

It has been a hobby of mine to create a video game console, but we live in a world where anyone can buy a $35 Raspberry Pi and install every 8, 16, and 32 bit game on it. So I have focused more on how consoles and software work and are made than actually making one. As part of this I have actually implemented an emulator of sorts, “Chip-8”.

Chip 8 was not a game console, but instead it is a byte-code interpreter that was originally run on 8-bit computers in the 1970s. It is, however, a good beginners project. My implementation took about 2 weeks to program and test. I have had a lot of success with running roms I have found online, and I am documenting it to share as a learning project for people that want to learn more about emulation, low level programming, or just like programming.

Now, enjoy my demo :

Posted in Blogroll

Undertow Websocket Client in Java SE

Undertow is a web server written in Java by JBoss. It uses a very modular architecture which allows the developer to pick and choose features they need so it fits anywhere from a Java EE web-server (Wildfly) to being embedded in a JavaSE application (the topic of this blog). Actually, this post isn’t about a web server AT ALL! This post is about using Undertow’s web socket library in a Java FX application to allow a server to send real time messages.

I’ve been working on a side project to make managing my Github notifications easier. The general idea is that a server will record my notifications, apply basic filtering on them, and then show me notifications ranked “Low”, “Medium”, and “High” priority. The UI of this application will be provided by Java FX and run standalone on the user’s system. A brief demo can be found on YouTube.

I originally tried the WebSocket implementation by TooTallNate, but they wouldn’t work correctly in my situation. I settled on the Undertow implementation mostly because I work for RedHat and can harass the developers on internal channels; however, this wasn’t needed.

The Undertow page mostly deals with server to server communication and the only WebSocket client example I could find that spoke to my situation was in a single unit test. However, I was able to cargo cult most of a working project, but I need to learn more about Xnio.

WebSocketProvider

The first thing I had to do was configure a XnioWorker. This static block is mostly cargo culture so I would refer to the official Xnio site before putting this into production.

    private static XnioWorker worker;

    static {
        try {
            worker = Xnio.getInstance().createWorker(OptionMap.builder()
                    .set(Options.WORKER_IO_THREADS, 2)
                    .set(Options.CONNECTION_HIGH_WATER, 1000000)
                    .set(Options.CONNECTION_LOW_WATER, 1000000)
                    .set(Options.WORKER_TASK_CORE_THREADS, 30)
                    .set(Options.WORKER_TASK_MAX_THREADS, 30)
                    .set(Options.TCP_NODELAY, true)
                    .set(Options.CORK, true)
                    .getMap());
        } catch (IOException | IllegalArgumentException ex) {
            Logger.getLogger(WebsocketProvider.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }
    }

Next, I created a static method to create a WebSocketClient instance and connect to my server. Because I am using self signed certificates as part of testing, I implemented a method to create an appropriate socket.

    public static WebSocketChannel getWebsocketClient(URI serverURI, String bearerToken, EventBus bus) {
        try {


            WebSocketClient.ConnectionBuilder builder = WebSocketClient.connectionBuilder(worker, new DefaultByteBufferPool(false, 2048), serverURI);
            builder.setClientNegotiation(new WebSocketClientNegotiation(null, null){
                @Override
                public void beforeRequest(Map<string , List<String>> headers) {
                    headers.put("Authorization", Lists.newArrayList("bearer " + bearerToken));
                }
            });

            if ("wss".equals(serverURI.getScheme())) {
                setupSSLSocket(builder);
            }

            WebSocketChannel channel = builder.connect().get();
            channel.resumeReceives();

            channel.getReceiveSetter().set(new AbstractReceiveListener() {
                @Override
                protected void onFullTextMessage(WebSocketChannel channel, BufferedTextMessage message) throws IOException {
                    bus.post(UPDATE_NOTIFICATIONS);

                }

                @Override
                protected void onText(WebSocketChannel webSocketChannel, StreamSourceFrameChannel messageChannel) throws IOException {
                    super.onText(webSocketChannel, messageChannel); //To change body of generated methods, choose Tools | Templates.
                    bus.post(UPDATE_NOTIFICATIONS);
                }

                @Override
                protected void onError(WebSocketChannel channel, Throwable error) {
                    super.onError(channel, error);
                    Logger.getLogger(WebsocketProvider.class.getName()).log(Level.SEVERE, error.getMessage(), error);

                }
            });

            return channel;
        } catch (IOException | CancellationException ex) {
            Logger.getLogger(WebsocketProvider.class.getName()).log(Level.SEVERE, null, ex);
            throw new RuntimeException(ex);
        }

    }

There are three pieces of code to pay special attention to channel.resumeReceives();, the WebSocketClientNegotiation implementation and the AbstractReceiveListener implementation. The first is necessary to receive messages from the server (I don’t know why, can someone from Undertow shed some light). The second adds a Bearer token authorization header so the server can authenticate the user with KeyCloak. The last is the actual handler for messages from the server. Currently it posts the message to an EventBus that various components are subscribed to.

There we have it! A very VERY simple websocket client for my Java FX application. If you want to play with it you can find the server and client source codes on my GitHub.

Posted in Blogroll

Reporting Gradle Builds using WebSockets

If you have a build server you might want to receive reporting from your build. Many build bots offer this kind of reporting, but I decided to implement it myself in a standard Gradle build script. I override the default logger and replace it with one that writes all of the logging to a web socket. I have also created a very simple Java EE service which can consume logging messages and rebroadcast them to a different web socket using JMS¹.

Gradle Configuration

buildscript {
    dependencies {
        /* I'm using the tyrus libraries for my web socket client.
         Because logging is part of the build and not the project, 
         they must be declared classpath and in the buildscript.dependencies
         stanza.    
        */
        classpath 'org.glassfish.tyrus:tyrus-client:1.+'
        classpath 'org.glassfish.tyrus:tyrus-server:1.+'
        classpath 'org.glassfish.tyrus:tyrus-container-grizzly:1.+'
    }
}
//Now we begin the setup for the WebSocket Logger
import org.glassfish.tyrus.client.*;

gradle.useLogger(new WebSocketLogger());

class WebSocketLogger implements org.gradle.api.logging.StandardOutputListener {

    def manager = ClientManager.createClient();
    def session = manager.connectToServer(WebSocketLoggerClientEndpoint.class, java.net.URI.create("ws://localhost:8080/log_viewer/logging"));

    // useLogger replaces the default logging.  I am writing to a tmp file for debugging purposes.
    def tmp = new File('/tmp/log.txt');

    @Override
    void onOutput(CharSequence charSequence) {
        tmp.append(charSequence +"\n");
        session.basicRemote.sendText(charSequence.toString());
    }

    @javax.websocket.ClientEndpoint
    class WebSocketLoggerClientEndpoint {

        @javax.websocket.OnMessage
        public void processMessageFromServer(String message, javax.websocket.Session session) {
            tmp.append(message +"\n");
        }

        @javax.websocket.OnError
        public void handleError(javax.websocket.Session session, Throwable thr) {
            tmp.append('Err' + thr.message +"\n");
        }
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

Java EE Server

The server side was more complex because of how CDI and WebSockets interact in Java EE 7. The code is really simple and benefits much more from browsing in GitHub than in snippets here. You may view the server source here : https://github.com/secondsun/log_viewer².

All this code does is take messages sent to the socket found at “ws://localhost:8080/log_viewer/logging” and rebroadcasts them to “ws://localhost:8080/log_viewer/read”

Conclusion

Being able to rebroadcast log messages is neat and useful. Additionally having a working example for connecting websockets to JMS was a lot of fun to put together.

Foot notes

1: I would have used CDI events, but CDI and the ServerEndpoint annotation do not get along. There are several JIRAs tracking this issue.
* WEBSOCKET_SPEC-196
* JMS_SPEC-121
* CDI-370
2: Thanks to https://blogs.oracle.com/brunoborges/entry/integrating_websockets_and_jms_with for help with getting this working.

Posted in Blogroll

AeroGear Android 3.0

So it has been a while, but AeroGear Android 3.0 is out. As fitting a major number release we have a few breaking changes, lots of bug fixes, and a few new features. The full changelist can be viewed on our JIRA page

Changes

Breaking Changes

New Features

  • aerogear-android-push now uses GCM 3 including GcmListener, InstanceID, and GCM Topics.
  • Android 23 support
  • Material design in cookbooks

Minor Changes

  • JUnit 4 based automated tests
  • Updates to all required libraries (Android SDK, Android Maven Plugin)

How to get it

In Android Studio just declare our dependencies in your build.gradle file. Feel free to mix and match as necessary.

    compile 'org.jboss.aerogear:aerogear-android-core:3.0.0'
    compile 'org.jboss.aerogear:aerogear-android-security:3.0.0'
    compile 'org.jboss.aerogear:aerogear-android-store:3.0.0'
    compile 'org.jboss.aerogear:aerogear-android-pipe:3.0.0'
    compile 'org.jboss.aerogear:aerogear-android-auth:3.0.0'
    compile 'org.jboss.aerogear:aerogear-android-authz:3.0.0'
    compile 'org.jboss.aerogear:aerogear-android-push:3.0.1'

Also, feel free to take our cookbook samples for a spin!

How to get involved

Feel free to join us at #aerogear on IRC, follow us @aerogears on Twitter, or also join our aerogear-dev and aerogear-users mailing lists. For more details please check out our Community Page.

Posted in Blogroll

Eventually Crappy People Show Up, and I’m Flattered

ShopToTrot has been out for a little more than a year now. We've gotten enough users for me to say that there is enough interest to keep going, and that feels really really killer. Like anything that attracts people online, some people that come through can be, well Crappy. There, I said it. By Crappy I mean that they have no intention of using the system as it was intended. They are there just to cause trouble.


Well ShopToTrot has received enough attention that we've gotten our first Crappy user that hung out just to put junk ads into the system. This one did have a sense of humor though. He or She at least mocked us a bit by putting in a photo of their "horse" (seen above) that mirrored our stock photo that we use if you don't put any pictures of your horse in (seen below).


For a moment, I was highly irritated. But that shortly gave way to flattery. Someone cared enough to come into our system and stink it up a bit. My point to all of this is simply, enjoy the Crappy people. They can't help themselves, and truly probably need a hug.

More to the point, they are a signal that you are becoming successful. And... if it weren't for them, you might not harden the system.
Posted in Blogroll

Supporting C in a build system

In a previous post, I showed that the standard build rules for C code are unreliable. Let me describe two ways to do better.

In the interest of brevity, I will describe the build rules using a toy build engine called Blcache (short for "build cache"). I initially tried to write this post using standard tools like Make, Ninja, Bazel, and Nix, but they all have one limitation or another that distracts from the main point of this post.

Example problem

Here is an example problem this post will work against. There is a test.c file, and it includes two separate header files:


// File test.c
#include <stdio.h>
#include "syslimits.h"
#include "messages.h"

int main() {
printf("%s: %d\n", MSG_LIMIT_THREADS, LIMIT_THREADS);
}

// File localhdrs/messages.h
#define MSG_LIMIT_THREADS "Limit on threads:"

// File hdrs/syslimits.h
#define LIMIT_THREADS 10

After compiling this code, a third header file is added as follows:


// File localhdrs/syslimits.h
#define LIMIT_THREADS 500

The challenge is for the addition of this header file to trigger a rebuild, while still making the build as incremental as possible.

Method 1: Compile entire components

The simplest way to get correct C compiles is to compile entire components at a time, rather than to set up build rules to compile individual C files. I've posted before on this strategy for Java, and it applies equally well to C.

This approach seems to be unusual, but my current feel is that it should work well in practice. It seems to me that when you are actively working on a given component, you should almost always use an IDE or other specialized tool for compiling that given component. The build system should therefore not concern itself with fine-grained incremental rebuilds of individual C files. Rebuilds of whole components--executables, libraries, and shared libraries--should be plenty, and even when using a system like Gyp, there are advantages to having the low-level build graph be simple enough to read through and debug by hand.

Using such an approach, you would set up a single build rule that goes all the way from C and H files to the output. Here it is in JSON syntax, using Blcache:


{
"environ": [
"PATH"
],
"rules": [
{
"commands": [
"gcc -Ilocalhdrs -Ihdrs -o test test.c"
],
"inputs": [
"test.c",
"localhdrs",
"hdrs"
],
"name": "c/test",
"outputs": [
"test"
]
}
]
}

The "environ" part of this build file declares which environment variables are passed through to the underlying commands. In this case, only PATH is passed through.

There is just one build rule, and it's named c/test in this example. The inputs include the one C file (test.c), as well as two entire directories of header files (localhdrs and hdrs). The build command for this rule is very simple: it invokes gcc with all of the supplied input files, and has it build the final executable directly.

With the build rules set up like this, any change to any of the declared inputs will cause a rebuild to happen. For example, here is what happens in an initial build of the tool:


$ blcache c/test
Started building c/test.
Output from building c/test:
gcc -Ilocalhdrs -Ihdrs -o test test.c

$ ./target/c/test
Limit on threads: 10

After adding syslimits.h to the localhdrs directory, the entire component gets rebuilt, because the localhdrs input is considered to have changed:


$ blcache c/test
Started building c/test.
Output from building c/test:
gcc -Ilocalhdrs -Ihdrs -o test test.c

$ ./target/c/test
Limit on threads: 500

As a weakness of this approach, though, any change to any C file or any header file will trigger a rebuild of the entire component.

Method 2: Preprocess as a separate build step

Reasonable people disagree about how fine-grained of build rules to use for C, so let me describe the fine-grained version as well. This version can rebuild more incrementally in certain scenarios, but that benefit comes at the expense of a substantially more complicated build graph. Then again, most developers will never look at the build graph directly, so there is some argument for increasing the complexity here to improve overall productivity.

The key idea with the finer-grained dependencies is to include a separate build step for preprocessing. Here's a build file to show how it can be done:


{
"environ": [
"PATH"
],
"rules": [
{
"commands": [
"gcc -c -o test.o target/c/preproc/test.i"
],
"inputs": [
"c/preproc/test:test.i"
],
"name": "c/object/test",
"outputs": [
"test.o"
]
},

{
"commands": [
"gcc -Ilocalhdrs -Ihdrs -E -o test.i test.c"
],
"inputs": [
"test.c",
"localhdrs",
"hdrs"
],
"name": "c/preproc/test",
"outputs": [
"test.i"
]
},

{
"commands": [
"gcc -o test target/c/object/test.o"
],
"inputs": [
"c/object/test:test.o"
],
"name": "c/test",
"outputs": [
"test"
]
}
]
}

This file has three rules in it that chain together to produce the final output file. When you build the test executable for the first time, all three rules will be executed:


$ blcache c/test
Started building c/preproc/test.
Output from building c/preproc/test:
gcc -Ilocalhdrs -Ihdrs -E -o test.i test.c

Started building c/object/test.
Output from building c/object/test:
gcc -c -o test.o target/c/preproc/test.i

Started building c/test.
Output from building c/test:
gcc -o test target/c/object/test.o

$ ./target/c/test
Limit on threads: 10

First, the test.c file is preprocessed, yielding test.i. Second, the test.i file is compiled to test.o. Finally, test.o is linked into the final test executable.

Adding the new syslimits.h file behaves as expected, causing the full chain of recompiles.


$ blcache c/test
Started building c/preproc/test.
Output from building c/preproc/test:
gcc -Ilocalhdrs -Ihdrs -E -o test.i test.c

Started building c/object/test.
Output from building c/object/test:
gcc -c -o test.o target/c/preproc/test.i

Started building c/test.
Output from building c/test:
gcc -o test target/c/object/test.o

$ target/c/test
Limit on threads: 500

Modifying an irrelevant header file, on the other hand, only causes the precompilation step to run. Since the precompilation yields the same result as before, rebuilding stops at that point.


$ touch localhdrs/irrelevant.h
$ blcache c/test
Started building c/preproc/test.
Output from building c/preproc/test:
gcc -Ilocalhdrs -Ihdrs -E -o test.i test.c

Using cached results for c/object/test.
Using cached results for c/test.

It's not shown in this example, but since each C file is compiled individually, a change to a C file will only trigger a rebuild of that one file. Thus, the technique here is fine-grained in two different ways. First, changes to one C file only trigger a recompile of that one file. Second, changes to the H files only trigger preprocessing of all C files, and then only compilation of those C files that turn out to be affected by the H files that were changed.

By the way, there's a trick here that generalizes to a variety of cached computations. If you want to add a cache for a complicated operation like a C compile, then don't try to have the operation itself be directly incremental. It's too error prone. Instead, add a fast pre-processing step that accumulates all of the relevant inputs, and introduce the caching after that pre-processing step. In the case of this example, the fast pre-processing step is, well, the actual C preprocessor.

Coda

Before realizing the problem with C compilation, I used C as an example of why you might want to break the rules a little bit about the most strict and simplistic version of a build cache. However, now it seems to me that you find the best set of build rules if you strictly adhere to a build-cache discipline. I'm sorry, build cache. I should never have doubted you.

Posted in Blogroll

Standard build rules for C are unreliable

The standard way of integrating C into a build system is to use automatic dependencies generated from the compiler. Gcc and Clang can emit a list of the header files they read if you run them with the -M option. Visual Studio can do it as well, using the /showIncludes option. What I will call the "standard approach" in this post is to use the dependencies the user explicitly declared, and then to augment them with automatic dependencies generated by options like -M or /showIncludes.

Until a few years ago, I just took this approach as received wisdom and didn't think further about it. It's a neat trick, and it works correctly in the most obvious scenarios. Unfortunately, I have learned that the technique is not completely reliable. Let me share the problem, because I figure that other people will be interested as well, especially anyone else who ends up responsible for setting up a build system.

The root problem with the standard approach is that sometimes a C compile depends on the absence of a file. Such a dependency cannot be represented and indeed goes unnoticed in the standard approach to automatic dependencies. The standard approach involves an "automatic dependency list", which is a file listing out the automatically determined dependencies for a given C file. By its nature, a list of files only includes files that exist. If you change the status of a given file from not existing, to existing, then the standard approach will overlook the change and skip a rebuild that depends on it.

To look at it another way, the job of a incremental build system is to skip a compile if running it again would produce the same results. Take a moment to consider what a compiler does as it runs. It does a number of in-memory operations such as AST walks, and it does a number of IO operations including reading files into memory. Among those IO operations are things like "list a directory" and "check if a file exists". If you want to prove that a compiler is going to do the same thing on a second run as it did on the first, then you want to prove that those IO operations are going to do the same thing on a second run. That means all of the IO operations, though, not just the ones that read a file into memory.

Such a situation may seem exotic. At least one prominent source has declared that the standard approach is "correct" up to changes in the build command, which suggests to me that the author did not consider this scenario at all. It's not just a theoretical problem, though. Let me show a concrete example of how it can arise in practice.

Suppose you are compiling the following collection of files, including a single C file and two H files:


// File test.c
#include <stdio.h>
#include "syslimits.h"
#include "messages.h"

int main() {
printf("%s: %d\n", MSG_LIMIT_THREADS, LIMIT_THREADS);
}

// File localhdrs/messages.h
#define MSG_LIMIT_THREADS "Limit on threads:"

// File hdrs/syslimits.h
#define LIMIT_THREADS 10
Using automatic dependencies, you set up a Makefile that looks like this:

CFLAGS=-Ilocalhdrs -Ihdrs

test.o test.d : test.c
gcc $(CFLAGS) -M test.c > test.d
gcc $(CFLAGS) -c test.c

test: test.o
gcc -o test test.o

-include test.d

You compile it and everything looks good:


$ make test
gcc -Ilocalhdrs -Ihdrs -M test.c > test.d
gcc -Ilocalhdrs -Ihdrs -c test.c
gcc -o test test.o
$ ./test
Limit on threads: 10
Moreover, if you change any of the input files, including either of the H files, then invoking make test will trigger a rebuild as desired.

$ touch localhdrs/messages.h
$ make test
gcc -Ilocalhdrs -Ihdrs -M test.c > test.d
gcc -Ilocalhdrs -Ihdrs -c test.c
gcc -o test test.o

What doesn't work so well is if you create a new version of syslimits.h that shadows the existing one. Suppose you next create a new syslimits.h file that shadows the default one:


// File localhdrs/syslimits.h
#define LIMIT_THREADS 500

Make should now recompile the executable, but it doesn't:


$ make test
make: 'test' is up to date.
$ ./test
Limit on threads: 10

If you force a recompile, you can see that the behavior changed, so Make really should have recompiled it:


$ rm test.o
$ make test
gcc -Ilocalhdrs -Ihdrs -M test.c > test.d
gcc -Ilocalhdrs -Ihdrs -c test.c
gcc -o test test.o
$ ./test
Limit on threads: 500

It may seem picky to discuss such a tricky scenario as this one, with header files shadowing other header files. Imagine a developer in the above scenario, though. They are doing something tricky, yes, but it's a tricky thing that is fully supported by the C language. If this test executable is part of a larger build, the developer can be in for a really difficult debugging exercise to try and understand why their built executable is not behaving the way that's consistent with the source code. I dare say, it is precisely such tricky situations where people rely the most on their tools behaving in an intuitive way.

I will describe how to set up better build rules for this scenario in a followup post.

Posted in Blogroll
AJUG Meetup

Building and Deploying 12 Factor Apps in Scala and Java

June 20, 2017

The twelve-factor app is a modern methodology for building software-as-a-service apps: • Use declarative formats for setup automation, to minimise time and cost for new developers joining the project. • Have a clean contract with the underlying operating system, offering maximum portability between execution environments. • Are suitable for deployment on modern cloud platforms, obviating the need for servers and systems administration. • Minimise divergence between development and production, enabling continuous deployment for maximum agility. • And can scale up without significant changes to tooling, architecture, or development practices. We will build a RESTful web service in Java and deploy the app to CloudFoundry. We will go over how to build a cloud manifest, how to keep our database credentials and application configuration outside of our code by using user-provided services and go over what it takes to build a 12 Factor application in the cloud. This presentation will be heavy on code and light on slides!
Location:

Holiday Inn Atlanta-Perimeter/Dunwoody 4386 Chamblee Dunwoody Road, Atlanta, GA (map)

AJUG Tweets

Follow @atlantajug on twitter.

Recent Jobs