Skip to content

Commit

Permalink
Port user guide pages from wiki
Browse files Browse the repository at this point in the history
  • Loading branch information
starkos committed Mar 17, 2021
1 parent c97edfa commit d9cab6a
Show file tree
Hide file tree
Showing 53 changed files with 3,540 additions and 391 deletions.
5 changes: 3 additions & 2 deletions website/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Premake website
# Premake Website

Premake website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator.

Expand All @@ -16,11 +16,12 @@ npm start

This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server.

To see a list of broken links (mistakes happen!), be sure to run `npm run build` before submitting updates.

## Build

```
npm run-script build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

14 changes: 14 additions & 0 deletions website/docs/Adding-New-Action.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
title: Adding a New Action
---

The Visual Studio, Makefile, and other exporters included in Premake are all "actions". They take the information from your project scripts and perform an action: in these examples, they output project files for specific toolsets.

Premake provides the ability to create your own actions. These can be simple one time operations like preparing a working copy of your source code for first use, or complex support for an entirely new toolset.

This tutorial walks through the process of creating a new action that outputs solution and project information as Lua tables, which you can then use Premake to read and manipulate if you wish.

* [Starting Your New Action](starting-your-new-action)
* [Generating Project Files](generating-project-files)
* Working with Configurations
* (More to come!)
46 changes: 46 additions & 0 deletions website/docs/Adding-Source-Files.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: Adding Source Files
---

You add files—source code, resources, and so on—to your project using the [files](files) function.

```lua
files {
"hello.h", -- you can specify exact names
"*.c", -- or use a wildcard...
"**.cpp" -- ...and recurse into subdirectories
}
```

You can use wildcards in the file patterns to match a set of files. The wildcard \* will match files in one directory; the wildcard \*\* will match files in one directory and also recurse down into any subdirectories.

Files located in other directories should be specified relative to the script file. For example, if the script is located at `MyProject/build` and the source files are at `MyProject/src`, the files should be specified as:

```lua
files { "../src/*.cpp" }
```

Paths should always use the forward slash `/` as a separator; Premake will translate to the appropriate platform-specific separator as needed.

## Excluding Files

Sometimes you want most, but not all, of the files in a directory. In that case, use the [removefiles()](removing-values) function to mask out those few exceptions.

```lua
files { "*.c" }
removefiles { "a_file.c", "another_file.c" }
```

Excludes may also use wildcards.

```lua
files { "**.c" }
removefiles { "tests/*.c" }
```

Sometimes you may want to exclude all the files in a particular directory, but aren't sure where that directory will fall in the source tree.

```lua
files { "**.c" }
removefiles { "**/Win32Specific/**" }
```
103 changes: 103 additions & 0 deletions website/docs/Adding-Unit-Tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: Adding Unit Tests
---

Premake includes an automated testing system that you can use the verify the behavior of your new module.


## Add your first test

Within our [Lucky module](introducing-modules) folder, create a new folder named `tests`.

Within that folder, create a new file named `tests/test_lucky_numbers.lua` with a simple failing test:

```lua
local suite = test.declare("lucky_numbers")

function suite.aFailingTest()
test.isequal(2, 3)
end
```

You'll also need a manifest to list all of your test files. Create another file in that same folder named `_tests.lua`:

```lua
lucky = require('lucky') -- replace with name of your module, obviously

return {
"test_lucky_numbers.lua",
}
```

When you're all done, your module should now look like:

```
lucky/
|- lucky.lua
`- tests/
|- _tests.lua
`- test_lucky_numbers.lua
```

## Enable the testing module

Premake's automated testing module is considered an advanced, developer-only feature which is not enabled by default. To enable it, you simply need to add the line `test = require("self-test")` somewhere it will be executed before your tests run.

The best place to put it is in your [system script](system-scripts), which will make the testing action available to all of your projects. But if that isn't feasible for you or your users, you can also place it in your project or testing script.

Premake's own code makes use of the latter approach: its `premake5.lua` script defines a custom action named "test", which in turn enables the built-in testing module:

```lua
newaction {
trigger = "test",
description = "Run the automated test suite",
execute = function ()
test = require "self-test"
premake.action.call("self-test")
end
}
```

## Run your test

Once the testing module is enabled, `cd` to your module folder and run the command `premake5 self-test`. You should see your simple failing test fail.

```
$ premake5 self-test
Running action 'self-test'...
lucky_numbers.aFailingTest: ...e/Premake/Modules/lucky/tests/test_lucky_numbers.lua:4: expected 2 but was 3
0 tests passed, 1 failed in 0.00 seconds
```

If developing new tests for premake itself, it is often beneficial to run smaller subsets of tests with the command-line option --test-only:

```
$ premake5 --test-only=lucky_numbers test
```

## Passing a test

To complete the example, let's replace our failing test with one which actually calls our module.

```lua
local suite = test.declare("lucky_numbers")

function suite.makesEightLucky()
local x = lucky.makeNumberLucky(8)
test.isequal(56, x)
end
```

And give it a go:

```
$ premake5 self-test
Running action 'self-test'...
1 tests passed, 0 failed in 0.00 seconds
```

## Next steps?

The `tests` folder in the Premake source code contains over 1,000 tests which you can use as examples. The ones in [`tests/actions/vstudio/vc2010`](https://github.com/premake/premake-core/tree/master/tests/actions/vstudio/vc2010) tend to be the most frequently updated and maintained, and generally make the best examples.

You can see the full set of test assertions (`test.isequal()`, `test.capture()`, etc.) in the Premake source code at [`modules/self-test/test_assertions.lua`](https://github.com/premake/premake-core/blob/master/modules/self-test/test_assertions.lua).
20 changes: 20 additions & 0 deletions website/docs/Build-Settings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
title: Build Settings
---

Premake provides an ever-growing list of build settings that you can tweak; the following table lists some of the most common configuration tasks with a link to the corresponding functions. For a comprehensive list of available settings and functions, see the [Project API](project-api) and [Lua Library Additions](lua-library-additions).

If you think something should be possible and you can't figure out how to do it, see [Getting Help](getting-help).

| | |
|-----------------------------------------------|----------------------|
| Specify the binary type (executable, library) | [kind](kind) |
| Specify source code files | [files](files), [removefiles](files) |
| Define compiler or preprocessor symbols | [defines](defines) |
| Locate include files | [includedirs](includedirs) |
| Set up precompiled headers | [pchheader](pchheader), [pchsource](pchsource) |
| Link libraries, frameworks, or other projects | [links](links), [libdirs](libdirs) |
| Enable debugging information | symbols(symbols) |
| Optimize for size or speed | [optimize](optimize) |
| Add arbitrary build flags | [buildoptions](buildoptions), [linkoptions](linkoptions) |
| Set the name or location of compiled targets | [targetname](targetname), [targetdir](targetdir) |
63 changes: 63 additions & 0 deletions website/docs/Code-Overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: Code Overview
---

## A Quick Tour of Premake ##

The Premake source code is organized into a few different folders:

* `src/actions` contains the code for the built-on actions and exporters, e.g. "vs2012" or "gmake". We are gradually migrating these into independent modules, but for now they live here.

* `src/base` contains the core Lua scripts, the code that is used to read and process your project scripts, and supporting logic for the actions and exporters.

* `src/host` contains all of the C language code, logic that either can't easily be written in Lua because of the way it needs to interact with the underlying operating system, or because a Lua implementation would be too slow. We try to keep C code to a minimum and use Lua whenever we can, to enable [overrides and call arrays](overrides-and-call-arrays).

* `src/tools` contains the adapters for command line toolsets like GCC and Clang. We will probably be migrating these toward modules in the near-ish future as well.

* `modules` contains the official set of modules which are distributed as part of Premake. These modules add support for additional languages and toolsets to the core code in the `src` folder.

In addition to those general categories, there are a few special files of note:

* `src/_premake_main.lua` contains the Lua-side program entry point, and is responsible for the main application flow. The C-side program entry point `main()` is located in `src/host/premake_main.c`.

* `src/_premake_init.lua` sets up much of the initial configuration, including all of the project scripting functions, the default set of command line arguments, and the default project configurations.

* `src/_modules.lua` contains the list of built-in modules which are automatically loaded in startup. See [Embedding Modules](embedding-modules) for more information.

* `src/_manifest.lua` lists the Lua scripts that should be embedded into the Premake executable when making the release builds. There are separate manifests for Premake's core scripts and each embedded module.


## Code Execution Overview ##

Execution starts at `main()` in `src/host/premake_main.c`, which calls into to `src/host/premake.c` to do the real bootstrapping work:

* `premake_init()` installs all of Premake's native C extensions to the Lua scripting environment.

* `premake_execute()` finds and runs `src/_premake_main.lua`, which may be embedded into the executable for a release build, or located on the filesystem.

* `src/_premake_main.lua` in turn reads `src/_manifest.lua` and loads all of the scripts listed there. Notably, this includes `src/_premake_init.lua` which does

* Once `src/premake_main.lua` has finished, `premake_execute()` calls `_premake_main()`, which located at the end of `src/_premake_main.lua`, and waits for it to return.

At this point, execution has moved into and remains in Lua; [extending Premake by overriding functions and call arrays](overrides-and-call-arrays) now becomes possible.

`_premake_main()` uses a [call array](overrides-and-call-arrays) to control the high-level process of evaluating the user scripts and acting on the results. Notable functions in this list include:

* `prepareEnvironment()` sets more global variables and otherwise gets the script environment ready to use.

* `locateUserScript()` handles finding the user's project script, i.e. `premake5.lua` on the file system.

* `checkInteractive()` is responsible for launching the REPL prompt, if requested.

* `runSystemScript()` runs [the user's system script](system-scripts), and `runUserScript()` runs the project script found by `locateUserScript()`.

* `processCommandLine()` handles any command line options and sets the target action and arguments. This needs to happen after the project script has run, in case it defines new options or modifies the behavior of existing options—a common point of confusion.

* `bake()` takes all of the project and configuration information that has been specified in the user's project script and prepares it for use by the target action, a somewhat convoluted process that is implemented in `src/base/oven.lua`.

* `validate()` examines the processed configuration information and tries to make sure it all makes sense, and that all required data is available. The main validation logic is located in `src/base/validation.lua`.

* `callAction()` passes each workspace, project, rule, and other container to the target action, causing the appropriate result--like generating a Visual Studio project or GNU makefile--to occur. This container iteration is done in `action.call()` in `src/base/action.lua`.

Calling the action, via `callAction()`, is where the interesting part for most people begins. Control now transfers to one of exporters, causing the project files to be written. For more information on how *that* happens, see [Creating a New Action](adding-new-action).

114 changes: 114 additions & 0 deletions website/docs/Coding-Conventions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
title: Coding Conventions
---

While not all of Premake's code currently follows these conventions, we are gradually nudging everything in this direction and hope to have it all done before the final 5.0 release. Knowing these conventions will make the code a little easier to read and follow.


### Tables as Namespaces

Premake tables are used as namespaces, with related functions grouped together into their own namespace table. Most of Premake's own code is placed into a table named `premake`. Code related to the project scripting API is in `premake.api`, code related to command line options in in `premake.options`, and so on.

Organizing the code in this way helps avoid collisions between similarly named functions, and generally helps to keep things tidy.


### Local Variables as Aliases

New namespaces are declared at the top of each source code file, followed by aliases for namespaces which are going to be used frequently within the source file. For example:

```lua
-- define a new namespace for the VC 2010 related code
premake.vstudio.vc2010 = {}

-- create aliases for namespaces we'll use often
local p = premake
local vstudio = p.vstudio
local project = p.project

-- and the "m" alias represents the current module being implemented
local m = p.vstudio.vc2010
```

The alias `p` is used conventionally as a shortcut for the `premake` namespace. The alias `m` is conventially used to represent the module being implemented.

Using aliases saves some keystrokes when coding. And since Premake embeds all of its scripts into the release executables, it saves on the final download size as well.


### Call Arrays

Premake's project file exporters—which write out the Visual Studio projects, makefiles, and so on—are basically big long lists of "output this, and then this, and then this". This could easily be written (and once was) as one giant function, but then it would be virtually impossible to modify its behavior.

Instead, we split up the generation of a project into many small functions, often writing out only a single line to the output. Any one of these functions can then be overridden by your own scripts or modules.

```lua
-- instead of this...

function m.outputConfig(cfg)
if #cfg.defines > 0 or vstudio.isMakefile(cfg) then
p.x('PreprocessorDefinitions="%s"', table.concat(cfg.defines, ";"))
end

if #cfg.undefines > 0 then
p.x('UndefinePreprocessorDefinitions="%s"', table.concat(cfg.undefines, ";"))
end

if cfg.rtti == p.OFF and cfg.clr == p.OFF then
p.w('RuntimeTypeInfo="false"')
elseif cfg.rtti == p.ON then
p.w('RuntimeTypeInfo="true"')
end
end

-- we do this...

function m.preprocessorDefinitions(cfg)
if #cfg.defines > 0 or vstudio.isMakefile(cfg) then
p.x('PreprocessorDefinitions="%s"', table.concat(cfg.defines, ";"))
end
end

function m.undefinePreprocessorDefinitions(cfg)
if #cfg.undefines > 0 then
p.x('UndefinePreprocessorDefinitions="%s"', table.concat(cfg.undefines, ";"))
end
end

function m.runtimeTypeInfo(cfg)
if cfg.rtti == p.OFF and cfg.clr == p.OFF then
p.w('RuntimeTypeInfo="false"')
elseif cfg.rtti == p.ON then
p.w('RuntimeTypeInfo="true"')
end
end

```

Similarly, instead of implementing the output of a particular section of the project as a function calling a long list of other functions, we put those functions into an array, and then iterate over the array. We call these "call arrays", and they allow you to inject new functions, or remove existing ones, from the array at runtime.

```lua
-- instead of this...

function m.outputConfig(cfg)
m.preprocessorDefinitions(cfg)
m.undefinePreprocessorDefinitions(cfg)
m.runtimeTypeInfo(cfg)
-- and so on...
end

-- we do this

m.elements.config = function(cfg)
return {
m.preprocessorDefinitions,
m.undefinePreprocessorDefinitions,
m.runtimeTypeInfo,
-- and so on...
}
end

function m.outputConfig(cfg)
p.callArray(m.element.config, cfg)
end
```

For an example of how to implement a new feature using these conventions, see [Overrides and Call Arrays](Overrides-and-Call-Arrays).
Loading

0 comments on commit d9cab6a

Please sign in to comment.