Creating a New Project

by Martin D. Maas, Ph.D

If you want to keep you code well organized, the first step is to create a project.

Basic Directory Structure

Let’s begin by creating a basic directory structure to store our project’s files. We will use (at least) the following folders:

  • src/ which will contain our main code, structured with modules and multiple files.
  • test/ to assess the functionality of our code.
  • docs/, most of which will be generated automatically from comments on the code.

Of course, this is a basic setup. Having tests and documentation is considered mandatory for good programming practices, but nothing stops you from adding additional folders.

Two common choices are:

  • examples/, to store simple scripts that demonstrate how to use the core project’s functionality.
  • benchmarks/, in case we need to keep track of performance of several cases, and/or to compare with competing alternatives.

Create the Source Tree and TOML files

Now let’s create the basic source tree, and the project’s TOML files

] 
pkg > generate MyProject

This will create the following source tree:

MyProject/
├── Project.toml
└── src
    └── MyProject.jl

MyProject.jl will contain a Hello World function named greet():

module MyProject

greet() = print("Hello World!")

end # module

The Project.toml and Manifest.toml are central to a project. While Project.toml can be edited manually, the Manifest.toml file is generated and maintained by Pkg, so should never be modified manually.

Enter the shell mode by typing ; in the REPL and create the remainder folders you need using mkdir.

;
shell> mkdir -p MyProject/{docs,test}

Activating the Project Environment

To activate the project environment, cd into project’s folder in shell mode, switch to pkg mode and type activate .:

shell> cd MyProject
] 
pkg > activate .

You can also type activate MyProject if you are on the parent folder. Your prompt should change into something of the form

(MyProject) pkg >

Equivalently, from the terminal, start Julia with the following command

julia --project=.

Adding dependencies

After activating the current environment, you can also add dependencies. For example, let’s add the Plots package.

] 
(MyProject) pkg > add Plots

Our Project.toml should now look like something

name = "MyProject"
uuid = "a5927f71-4e94-4484-9e8d-767c56d7f0c4"
authors = ["yourname <[email protected]>"]
version = "0.1.0"

[deps]
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"

Using your project

Once activated you activate the project environment, you can now call your greet function with the following code:

using MyProject
MyProject.greet()

Did you modify MyProject.jl? If you are working interactively, testing things and adding them to your project’s module, you might notice that calling using MyProject again doesn’t do anything. This can be annoying.

In order to force the recompilation of your project’s files every time you call using MyProject from the REPL, you need to include the Revise.jl package to your working environment.

Simply start Julia, and install the Revise package in the global environment (that is, there is no need to install it as a project dependency), then activate the project environment and do:

using Revise
using MyProject
MyProject.greet()

###
### Introduce modifications in the file MyProject.jl
###

using MyProject
MyProject.greet()   

Now, the second time you call the function, the changes you’ve made in MyProject.jl will be reflected properly.

Export

You might have noticed that, when we want to call our function greet() defined in MyProject, we need to reference it using MyProject.greet().

This happens by default to keep the global namespace clean. But we might want to change that.

In order to do it, we need to explicitly export a symbol, using the export keyword. So our project should look like this:

module MyProject

greet() = print("Hello World!")

export greet

end # module

And now, from any other scope, we can do

using MyProject
greet()   

Conclusion

Ok, we’ve got the basics covered. In subsequent posts we will go about structuring our code into modules, and cover some good programming practices like writing tests and relying on automatically-generated documentation. So stay tuned!