Skip to content
FutureBlitz

    Install

    Modules and packages, 4 of 6

    project.toml

    Make a package with fb new, name its modules by its name, and set what project.toml controls.

    What you’ll build

    A package called shop, made with fb new, with a module of prices beside its main file. You’ll see what project.toml holds, how a package’s modules are named, where fb build puts the executable, and what fb says when project.toml is wrong.

    A new package

    fb new makes a package in a new folder, named after the folder:

    fb new shop

    The folder has three files: main.fb, which prints a greeting; a .gitignore that leaves out build/; and project.toml:

    [project]
    name = "shop"
    version = "0.1.0"

    A package is a folder with a project.toml. name is an identifier other than std, and version is three numbers, like 0.1.0. Inside the folder, fb run builds the package and runs it, and fb build writes the executable to build/shop, or build\shop.exe on Windows.

    Modules named by the package

    A package’s files are modules named by its name and their paths, so prices.fb in shop is shop.prices, and money/tax.fb would be shop.money.tax. Other files of the package import it by that name.

    project.toml
    [project]
    name = "shop"
    version = "0.1.0"
    prices.fb
    Import std.core
    
    Public Const TAX_PERCENT = 8
    
    Public Function WithTax(cents: Int): Int => cents + cents * TAX_PERCENT / 100
    
    Public Function Dollars(cents: Int): String
        Local rest = cents % 100
        Return "${cents / 100}.{rest < 10 ? "0" : ""}{rest}"
    End Function
    main.fb
    Import std.core
    Import shop.prices
    
    Local price = 1999
    Print("{Dollars(price)} is {Dollars(WithTax(price))} with {TAX_PERCENT}% tax")
    Output
    $19.99 is $21.58 with 8% tax

    Import "./prices" works too, by path. The package’s name is the usual way, since it reads the same from every file, however deep in folders.

    The main file

    The main file is main.fb unless main names another, by its path in the package’s folder. Its top-level code is the program.

    [project]
    name = "shop"
    version = "0.1.0"
    main = "src/app.fb"

    A package without a main file is a library: its modules are for other packages to use, and it builds no program. type = "library" says so, and is the default for a package with no main file; type = "executable" is the default for one with a main file. Publishing a package makes a library.

    The formatter’s width

    [format] holds one key, width, the line width fb fmt formats the package’s code to. It’s 100 columns without it, and can be 40 to 1000.

    [project]
    name = "shop"
    version = "0.1.0"
    
    [format]
    width = 80

    When project.toml is wrong

    fb reads project.toml before any code, and reports its mistakes at their lines, as it does the code’s. A key it doesn’t use is a warning, so a misspelling doesn’t go unnoticed, and a value it can’t use is an error, which stops the build. This project.toml has one of each:

    project.toml
    [project]
    name = "shop"
    version = "1.0"
    author = "Ada"
    main.fb
    Import std.core
    
    Print("open")
    $ fb run
       Compiling .
    project.toml:3:11: error: a package's version is three numbers, like "0.1.0", and maybe a pre-release, like "1.0.0-beta.1", not "1.0"
      |
    3 | version = "1.0"
      |           ^^^^^
    project.toml:4:1: warning: [project] doesn't use 'author'
      |
    4 | author = "Ada"
      | ^^^^^^
    error: couldn't build . because of 1 error

    What you learned

    • fb new makes a package: a folder with project.toml and main.fb.
    • [project] needs name and version, and can set main and type.
    • A package’s modules are named by its name and their paths, as shop.prices.
    • fb build writes the executable to build/, named after the package.
    • [format] width sets the line width for fb fmt.

    Report an issue with this page or with FutureBlitz on GitHub.