Vala.dev A personal blog from a hobbist developer.

Diving Into Vala: Project Lovey v0.1.1

Refactor the source & setup a Meson build system.


Posted by David Rodriguez

Something to think about

So, here we are. Back at Project Lovey (if you haven’t read part one, be sure to check it out) and I still have no idea what I want her to do! But I do know that there are certain things I want this app to have.

I want this app to have:

  • A Custom HeaderBar
    • Settings/Menu Button
      • Popover Menu
    • Info/About Button
      • An About App Dialog
      • App Licensing Info
      • Contributor Info
  • A Welcome Page
    • Action items?

This can be a good start to a template for future apps. Since I have no idea what I want the app to do, but I do know that it will just be a simple single windowed app, I will basically build a template for now.

Before we get started

Vala does not enforce a certain source code structure in which to place your files in. There is no recommended software architecture / design pattern. To paraphrase the Vala Tutorial, you are free to structure your source code in whatever way suites you and/or your projects needs.

“Vala code is written in files with .vala extensions. Vala does not enforce as much structure as a language like Java - there are no concepts of packages or class files in the same way. Instead structure is defined by text inside each file, describing the logical location of the code with constructs such as namespaces. When you want to compile Vala code, you give the compiler a list of the files required, and Vala will work out how they fit together.”

Vala Tutorial

This can be both a good thing, and a bad thing! This means you can do something lazy like, placing all of your source files into a single directory and compile them. Because, believe it or not, that is a valid project structure!

For ‘small projects’ – I would consider <=5 .vala source files to be a small project – bunching your source files into the root of a project folder shouldn’t be too much of an issue. As far as myself, no matter how big or small the project may be, I like to structure the source code into a logical folder hierarchy. The only time that I do not do this is when the project meets the ‘small project’ requirements, as stated above.

What we will cover in this article

Part one got us set up with a basic Vala & GTK+ 3 GUI window. In this second part of the series, we will refactor this code and prepare it for further development. As it stands, we can write the entire application in one huge file. Nothing is stopping us from doing this. Except the fact that the source code would get unwieldy large and overly cumbersome (to say the least) to manage.

So for this reason, we’re going to break our project’s source code down into logical and manageable chunks. Each in their own separate .vala files. After the refactoring is finished, we will focus on setting up Meson for this project.

Later on in this series we will cover adding custom widgets to the window and buttons to the header bar. But for now, let’s just try refactoring the code and setting up a build system for our project.

Getting started (Project Prepping).

The current state of the project-lovey directory should look like so:

project-lovey/
├── CHANGELOG.md
├── LICENSE.md
├── Lovey.vala
└── README.md

0 directories, 4 files

Let’s switch this up a little. We’re going to create a src directory inside of the project-lovey directory that will contain all of our source code from herein out.

$ mkdir src
$ cd src/

Refactoring the Project Lovey code structure

In order to refactor the Lovey.vala code, I am going to create 3 new files in the new src directory.

  • Application.vala
  • Main.vala
  • Window.vala
$ touch Application.vala Main.vala Window.vala

Your project-lovey directory should look similar to this:

$ tree ~/Projects/project-lovey/
project-lovey/
├── CHANGELOG.md
├── LICENSE.md
├── Lovey.vala
├── README.md
└── src
    ├── Application.vala
    ├── Main.vala
    └── Window.vala

1 directory, 7 files

Namespaces

Namespaces in Vala, are named scopes. To quote the Vala Reference Manual directly,

“Definitions in different namespaces can use the same names without causing conflicts. A namespace can be declared across any number of Vala source files, and there can be multiple namespaces defined in a single Vala source file. Namespaces can be nested to any depth. When code needs to access definitions from other namespaces, it must either refer to them using a fully qualified name, or be written in a file with an appropriate using statement.” - Vala Reference Manual

Although we will be touching on Namespaces within our code. We will be using an alternative syntax, as compared to the default syntax.

The default namespace syntax:

namespace Lovey {
  public class Application : Gtk.Application {
    // Code goes here...
  }
}

I will be using the dot notation syntax, refering to the fully qualified name when needed, like so:

public class Lovey.Application : Gtk.Application {
  // Code goes here...
}

I personally find it to be much neater and cleaner than wrapping the code in a namespace scope. So I will be using the alternative syntax. You can use whatever syntax you'd like for the namespace. This is solely a personal choice.

Let's begin refactoring

When we're done, nothing about the app should change (okay, I am going to change the App's title and the message in the center of the screen). The only thing that you should focus on is how the code is structured and broken down into seperate files.

We can actually copy and paste a lot of the code from Lovey.vala to avoid rewriting things. Not much changes, except for a few lines. So let’s start with the easiest change, the Main.vala file. All we have to do is take the main function from the Lovey.vala file and place it here, and wrap it in the Lovey namespace.

/*
 * File: Main.vala
 * Author: David Saul Rodriguez <david@enyutech.io>
 *
 * Description: The main function (starts the app).
 *
 * This file is subject to the terms and conditions defined in
 * file 'LICENSE.md', which is part of this source code package.
 *
 * Copyright (c) 2019 David Saul Rodriguez <david@enyutech.io>. All Rights Reserved.
 */
public static int main(string [] argv) {
  var app = new Lovey.Application ();
  return app.run (argv);
}

That's it. Save the file and close it. Let's move on to the Window.vala file.

Now, open up the Window.vala file and type in the following code (don't forget to wrap the Window class into the Lovey namespace). We're also going to change the title of the application to “Lovey Refactored” and the label's text in the middle of the window, to “Hello Again, I’m refactored!” (this is optional).

/*
 * File: Window.vala
 * Author: David Saul Rodriguez <david@enyutech.io>
 *
 * Description: Constructs the main window for the application.
 *
 * This file is subject to the terms and conditions defined in
 * file 'LICENSE.md', which is part of this source code package.
 *
 * Copyright (c) 2019 David Saul Rodriguez <david@enyutech.io>. All Rights Reserved.
 */
 public class Lovey.Window : Gtk.ApplicationWindow {

   public Window (Application app) {
     Object (
       application: app
     );
   }

   construct {
     set_title ("Lovey Refactored");
     set_default_size (640, 480);
     window_position = Gtk.WindowPosition.CENTER;

     Gtk.Label label = new Gtk.Label ("Hello Again, I'm refactored!");
     add (label);
     show_all ();
   }

 }

For now, the Window.vala is all set. You can save it and now open the Application.vala file and type (or copy and paste) the following code in it:

/*
 * File: Application.vala
 * Author: David Saul Rodriguez <david@enyutech.io>
 *
 * This file is subject to the terms and conditions defined in
 * file 'LICENSE.md', which is part of this source code package.
 *
 * Copyright (c) 2019 David Saul Rodriguez <david@enyutech.io>. All Rights Reserved.
 */
public class Lovey.Application : Gtk.Application {

 public Application () {
   Object (
     application_id: "dev.vala.lovey",
     flags: ApplicationFlags.FLAGS_NONE
   );
 }

 protected override void activate () {
   Lovey.Window window = new Lovey.Window (this);
   add_window (window);
 }
}

Compiling the code.

Now we can head into the src directory and run the following command:

$ valac -o Lovey --pkg=gtk+-3.0 Application.vala Main.vala Window.vala

If everything compiles correctly you can now run this application with the following command:

$ ./Lovey

And there you have it! Your app has been refactored and should now look like: Simple Window Refactored

Note: You can now delete the Lovey.vala file that we began with, in the project’s root directory.

It’s time for a build system!

So, why do we need a build system? A build system is going to make things really easy for us! It’s going to save us a lot of time from typing in repeated commands and adding new files to the compile command every time we modify, or add a new source file.

The new Meson build system.

As far as build systems go, we will be using the Meson build system. It is far easier to configure than Make, and far less error prone. Not to mention, it comes with support for Vala out of the box.

If you don’t have Meson installed, you can so do with the following command for Debian/Ubuntu based systems:

$ sudo apt install meson

Setup and configure Meson for our project.

At the root of the project-lovey directory create a meson.build file which should then make your project’s root directory look like so:

$ tree ../project-lovey
../project-lovey/
├── CHANGELOG.md
├── LICENSE.md
├── meson.build
├── README.md
└── src
    ├── Application.vala
    ├── Main.vala
    └── Window.vala

1 directory, 7 files

and open up that meson.build file and input (or copy and paste) the following:

project ('dev.vala.lovey', 'vala', 'c')

executable (
  meson.project_name (),

  'src/Main.vala',
  'src/Window.vala',
  'src/Application.vala',

  dependencies: [
    dependency ('gtk+-3.0')
  ],
  install: true
)

Let’s give Meson a try!

Now that we have our meson.build file all setup. Let’s compile our project using Meson. From the root directory of the project, run the following command (which should also produce the similar output):

$ meson build --prefix=/usr
The Meson build system
Version: 0.49.2
Source dir: /home/drodriguez/Projects/project-lovey
Build dir: /home/drodriguez/Projects/project-lovey/build
Build type: native build
Project name: dev.vala.lovey
Project version: undefined
Native C compiler: cc (gcc 7.4.0 "cc (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0")
Native Vala compiler: valac (valac 0.40.15)
Build machine cpu family: x86_64
Build machine cpu: x86_64
Found pkg-config: /usr/bin/pkg-config (0.29.1)
Dependency gtk+-3.0 found: YES 3.22.30
Build targets in project: 1
Found ninja-1.8.2 at /usr/bin/ninja

Once the meson build command is complete, you should now have a build/ folder in your projects root directory. Now change into that directory and run the ninja command.

$ cd build/
$ pwd
~/Projects/project-lovey/build
$ ninja
[5/5] Linking target dev.vala.vala-dive.
$

The ninja command will make an executable file for you, with the same name as your project’s name inside of your meson.build file. Now you can run the executable named dev.vala.lovey using this command:

$ ./dev.vala.lovey

Summary

Later in this series, I will show you how to expand on Meson to include sub-directories and other things such as post install scripts and whatnot. But for now, this meson.build file should do just fine for our purposes.

And there you have it! Our very own Project Lovey (v0.1.1) update is complete. The code has been refactored and our meson build system is all setup and working. Now when we make any changes to the code, we only need to run ninja again from within the build/ folder to recompile our project.