How can I tell nodemon to ignore all node_modules except for one directory - nodemon

I have a Node.js server I am developing and I'm using nodemon to have the server restarted when the source code changes.
This server has a dependency on another module, which I am also developing, so it would be nice if changes to that module also restarted the server.
Best attempt yet (Thanks, bitstrider!)
I made a repo to reproduce this problem with minimal code if anyone wants to check it out: https://github.com/shawninder/nodemon-troubles
The highlights are in nodemon.json:
{
"ignoreRoot": [
".git",
".nyc_output",
".sass-cache",
"bower-components",
"coverage"
],
"ignore": [
"node_modules/!(is-node)",
"node_modules/is-node/node_modules"
]
}
ignoreRoot is the same as the defaults, but without node_modules.
ignore is where I'm trying to tell it to ignore node_modules, but not is-node.
This almost works (editing is-node restarts the server) but it's watching all the node modules, not only is-node.
How can I tell nodemon to ignore all node modules except for one?
Details
I'm using the latest version of nodemon, 1.11.0
I also tried with https://github.com/remy/nodemon/pull/922
Explaining the problem
Nodemon takes the ignores and combines them into one big regular expression before passing this along to chokidar. It's a custom parsing algorithm based on regular expressions and supports neither glob negation nor regular expression syntax. For example, the example above produces the following regular expression:
/\.git|\.nyc_output|\.sass\-cache|bower\-components|coverage|node_modules\/!\(is\-node\)\/.*.*\/.*|node_modules\/is\-node\/node_modules\/.*.*\/.*/
Notice the negation character ! end up as a literal ! in the resulting regular expression. Using negative look-ahead syntax also doesn't work because the control characters are transformed into literal characters.

Since nodemon uses minimatch for matching, you should be able to use negation with the symbol !, according to the documentation.
So in your case, try:
{
"ignore" : "node_modules/!(my-watched-module)/**/*"
}
This should ignore all module files except those within my-watched-module
NOTE: The ignore rule here only targets files, not directories. This behavior makes sense in the context of nodemon because its watching for file changes.

I was able to solve this problem by using NPM's built-in workspaces feature.
Update NPM to the latest version. NPM must be on at least version 7.x to use workspaces.
If a ./node_modules directory doesn't already exist, create it in your project's root folder.
Create a directory outside of ./node_modules (Nodemon ignores this directory by default) to house your under-development dependency module(s).
For example, assuming Nodmon is running in the same folder as your project's package.json file, you could create a new folder named, ./node_modules_custom, in the same directory.
Give each module ① its own package.json file and ② its own folder. NPM will rely on these package.json files to distinguish between your module(s) and anything else that may exist in the parent directory.
My_Example_Node_Project/
├─ package.json/
├─ node_modules_custom/
│ ├─ my_custom_module_1/
│ │ ├─ package.json
│ │ ├─ non_standard_main_file.js
│ ├─ my_custom_module_2/
│ │ ├─ package.json
│ │ ├─ index.js
├─ node_modules/
├─ index.js
├─ .git/
├─ .gitignore
├─ .gitattributes
Designate the folder containing your under-development dependency modules as a workspace by adding the following three lines to package.json, where node_modules_custom is the name of the directory created in Step 3.
"workspaces": [
"node_modules_custom/*"
]
Your full package.json file should now look something like this:
{
"name": "my_example_node_project",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/USER/my_example_node_project.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/USER/my_example_node_project/issues"
},
"homepage": "https://github.com/USER/my_example_node_project#readme",
"workspaces": [
"node_modules_custom/*"
]
}
Run npm install in your project's root directory. NPM will scan-through any directories designated as a workspaces (Step 5) and create symbolic links for the module folders therein.
If the process was completed successfully ./node_modules will now contain your under-development dependency module folders as symbolic links. When the contents of any of these folders is altered, Nodemon will automatically detect it and update accordingly. Any non-symbolically-linked files or folders will continue to be ignored.

To watch a specific dir under node_modules while continuing to ignore the rest of node_modules, add the negation flag to the node_modules path in ignoreRoot. Specifying ignoreRoot overrides the default, so include the other files that are part of the default.
{
"watch": ["src", "node_modules/(package_to_watch)"],
"ignoreRoot": [
".git",
".log",
".nyc_output",
".sass-cache",
".yarn",
"bower_components",
"coverage",
"node_modules/!(package_to_watch)"
]
}
Thanks #strider for cluing me in on the negation flag.

Related

How to run tests in a multimodule Go project

I'm learning Go with the specialization Programming with Google Go. It has several courses, each of which has several modules.
My project looks like the following (made with https://ascii-tree-generator.com/):
google-golang/
├─ .github/
│ ├─ workflows/
│ │ ├─ ci.yml
├─ golang-getting-started/
│ ├─ module1/
│ │ ├─ main.go
│ │ ├─ main_test.go
│ ├─ module2/
│ │ ├─ trunc/
│ │ │ ├─ main.go
│ │ │ ├─ main_test.go
├─ .gitignore
├─ README.md
I'd like to run all the tests in the *_test.go files for every commit, and it's not clear to me how to do that from the root directory (google-golang). I don't see a need for one module to import another as the exercises can be done independently. This question has two answers, one suggests using submodules, another recommends using Go workspace, but neither provide specific instructions that someone new like me can learn from.
I'm not asking for help on GitHub Actions, I know how to write those. I'm looking for one or more commands that'll find and run the tests.
You seem confused about what a module is. The rule of thumb is, one go.mod file equals one module. Go Wiki, gomod:
A module is defined by a tree of Go source files with a go.mod file in the tree's root directory.
Based on your directory tree shown in your question, there is no go.mod file in sight, hence nothing there is a Go module. As a matter of fact, if you attempt running a module-aware command from google-golang or golang-getting-started you'll have:
go: go.mod file not found in current directory or any parent directory; see 'go help modules'
If you want to run all tests from the root of a multi-module repo, as the title of your question says, with Go 1.17 you can:
init the sub-modules:
$ cd google-golang/golang-getting-started/module1
$ go mod init example.com/module1
$ cd ../module2
$ go mod init example.com/module2
use the trick suggested by Cerise Limón from the root dir of the multi-module project
$ cd google-golang
$ find . -name go.mod -execdir go test ./... \;
If you don't actually care about keeping the sub-repos as separate modules, you can init a module in the root repo and run all tests from there:
$ cd google-golang # or google-golang/golang-getting-started/
$ go mod init example.com/mainmod
$ go test ./...
...however, even this hack works right now, it doesn't make a lot of sense, because your repos named moduleN have main.go files, whose packages are supposedly named main, in each of them. So they are indeed organized as separate sub-modules.

Terragrunt path resolution - locally referenced modules - splitting into multiple environments

For business-related reasons, I am not able to split my infrastructure using versioned modules. As it is still beneficial to split the environments, and I would like to avoid the copy/paste root modules, which are basically just instantiation of child modules, over multiple directories, each representing its own environment, I would like to specify the source of root module in terragrunt.hcl
To make life easier, I have built a small part of the current infrastructure, just with a single module to speed up the development.
The current structure of the project looks like this:
├── config
│ ├── common.tfvars
│ └── terragrunt.hcl
├── environments
│ └── dev
├── infrastructure-modules
│ └── ecs
├── terraform-modules
│ ├── terraform-aws-ecr
All my infrastructure is described in infrastructure-modules/ecs/*.tf files, which are basically just instantiating child modules declared in terraform-modules/terraform-aws-*/.
With that, I can simply execute the terragrunt (terraform commands) from the infrastructure-modules/ecs directory.
To have a possibility to create the same environment in another account, I have introduced a new directory environments/dev/eu-central-1/ecs as shown on tree output from the root directory.
The environments/dev/eu-central-1/ecs, consists just of two files, terragrunt.hcl and common.tfvars.
I guess, that the usage of the common.tfvars is quite self-explanatory, where my terragrunt.hcl consists of remote_state {} and terraform {} blocks.
The important part of the terragrunt configuration file:
remote_state {}
terraform {
source = "../../../../infrastructure-modules/ecs"
{...}
}
Above I am basically referencing my root modules, declared in infrastructure-modules/ecs/*.tf. Where my root modules are instantiating child-modules declared in terraform-modules/terraform-aws-*/.
Child modules from infrastructure-modules/ecs/*.tf are instantianed like this:
module my_module {
source = "../../terraform-modules/terraform-aws-*"
{...}
}
In an ideal world, I would be able to execute terragrunt (terraform) commands from environments/dev/eu-central-1/ecs directory, but as I am using local (relative) paths, this is failing during the initialization of the modules, as the root module my_module loads the child module with following relative path:
module my_module {
source = "../../terraform-modules/terraform-aws-*"
{...}
}
This is causing a module instantiation in environments/dev/eu-central-1/ecs to fail as the relative path is different, based on parent module instantiation.
Initializing modules...
- my_module in
Error: Unreadable module directory
Unable to evaluate directory symlink: lstat ../../terraform-modules: no such
file or directory
So far, according to the documentation, path_relative_*, should be able to return the relative path between the path specified in its include block and the current terragrunt.hcl, but the problem here is that I am not having any include {} block(s) within my terragrunt.hcl files and thus this approach doesn't works. Symlinks are the last option.
EDIT
If I inspect the .terragrunt-cache/* on path environments/dev/eu-central-1/ecs I can confirm that all the "root" modules have been downloaded(copied over) into cache directory.
However, the module is being instantiated like this, and it tries to fetch the actual modules (Terraform modules) from directory two levels above.
module my_modules {
source = "../..//terraform-modules/terraform-aws-ecr"
So basically, I need to tell Terragrunt to download/fetch the modules from other path.
EDIT 2:
Inspecting .terragrunt-cache in the directory where I am running init shows, the the terraform-modules are never downloaded in the
terraform-infrastructure/environments/dev/eu-central-1/ecs/.terragrunt-cache/.
If I change my terraform-infrastructure/infrastructure-modules/ecs/ecr-repos.tf, from
module ecr_lz_ingestion {
source = "../../terraform-modules//terraform-aws-ecr"
{<MODULE_ARGUMENTS>}
}
}
to:
module ecr_lz_ingestion {
source = "../../../../../../../../terraform-modules//terraform-aws-ecr"
{<MODULE_ARGUMENTS>}
}
}
Terraform is able to initialize the child-modules as I have given a relative path to terraform-modules/ in the directory root, which is obviously a workaround.
Somehow I am expecting the Terragrunt to download both directories, terraform-modules and infrastructure-modules for the relative paths in module instantiation to work.
Based off the additional information you provided, I understand that this is your current directory structure:
terraform-infrastructure
├── config
│ ├── common.tfvars
│ └── terragrunt.hcl
├── environments
│ └── dev
│ └── eu-central-1
│ └── ecs
│ └── terragrunt.hcl
├── infrastructure-modules
│ └── ecs
├── terraform-modules
│ ├── terraform-aws-ecr
│
├── terragrunt.hcl
Notice the terragrunt.hcl in the parent directory that I added.
That terragrunt.hcl is considered the parent file and can include code that can be shared among other terragrunt files.
It can include something like this:
remote_state {}
In your eu-central-1/ecs folder, add the following to your terragrunt file:
include {
// searches up the directory tree from the current terragrunt.hcl file
// and returns the absolute path to the first terragrunt.hcl
path = find_in_parent_folders()
}
// using the path relative from the path stated in the include block
terraform {
source = "${path_relative_from_include()}//infrastructure-modules”
}
This should keep relative pathing intact when child modules are instantiating.
Edit:
From your GitHub issue, it would be best to either move the double slash to somewhere the modules share a common path with. Either that or to just consolidate your modules into a single folder.

How to handle semi-relative include paths with cppcheck

Our C++ project is organized in several modules (== subfolders) and headers are placed next to the .cpp files:
CMakeLists.txt
src
│
└───folder1
│ │
│ └───subfolder1
│ │ MyClass1.h
│ │ MyClass1.cpp
│ │ ...
│
└───folder2
│ │
│ └───subfolder2
│ │ MyClass2.h
│ │ MyClass2.cpp
│ │ ...
Include directives are always defined relative to the folder src and not relative to the code file e.g. in MyClass1.cpp:
#include "folder1/subfolder1/MyClass1.h" // even the own header is defined semi-relatively
#include "folder2/subfolder2/MyClass2.h"
MyClass1::MyClass1() {
// some code
}
I recently noticed that cppcheck (version 1.89) has problems with this and does not correctly
resolve macros defined in a header file -> False complaints about correct code
find problems with class member initialization (e.g. MyClass::MyClass() : _foo(_foo) {}) -> No complaints about incorrect code
When providing -I src to the cppcheck CLI, macros are correctly identified and actual issues like above are found, but analysis time skyrockets from 2 to 20 minutes.
I suspect, that by providing the whole source code again via -I, the files are all re-parsed as header files. Unfortunately, I don't have a specific include/ sub folder which I can use here. What is advised here? I am already using multiple jobs: -j 4.
After some further investigation, this might actually not be a valid question:
I manually took all header files and copied them to a separate folder: find . -type f \( -iname \*.h -o -iname \*.inl -o -iname \*.hpp \) -exec cp --parents \{\} ./../__cppcheckWorkaroundInclude \; which I then provided via -I to cppcheck
The check still took 20 minutes, so my assumption that cppcheck wrongfully scans too much is invalid - it actually takes this long because it is a lot of code.
So providing -I __cppcheckWorkaroundInclude or -I src to cppcheck did not make a difference - at least I did not observe one. Meaning, that -I works as expected
To solve the performance issues (I run the job inside a CI and 20 minutes is a lot of time which I would rather spend on executing automated tests), I did the following:
Reduce the checked configurations --max-configs=5
Used incremental checking via --cppcheck-build-dir=CppcheckBuildDir
To integrate this in our Gitlab CI, I had to cache this directory:
cache:
paths:
- CppcheckBuildDir
and before the cppcheck call, the directory has to be created (but with the -p flag because the dir might already be there, taken from the cache!):
mkdir -p CppcheckBuildDir

How to build Modernizr with bower and brunch - missing main in bower.json

I've read this github issue stating:
there isn't a main file to be in the main.
I have modernizr 3.0.0 installed automatically as a dependency of Foundation 5.5.2 and am trying to build my project with Brunch.
When running brunch build I get the following error:
Error: Component JSON file "/path/to/brunch-test/bower_components/modernizr/.bower.json" must havemainproperty. See https://github.com/paulmillr/read-components#README
So following the read-components issue, I am trying to override modernizr's main in my root bower.json but not sure how to go about it as there's no simple compiled modernizr.js present.
I know modernizr is meant to be customized, and indeed the modernizr 3 release news state there is a really cool solution of dynamically creating a custom package that can be installed via bower but I'm unable to find information about this?
Ok, I figured it out.
So my directory tree is something along the lines of (simplified):
/
|-- bower.json
|-- bower_components
|-- modernizr
|-- bin
|-- modernizr
I went into bower_components/modernizr and ran npm install to get the dependencies required to run the bin/modernizr builder.
Then I went to their website to pick out the features I required: https://modernizr.com/download?setclasses
Next, I clicked Build and downloaded the Command Line Config which I placed at the root directory of my project as modernizr-config.json.
Then I ran bin/modernizr -c ../../modernizr-config.json which placed a custom built modernizr.js in /bower_components/modernizr/modernizr.js
Finally, in my root bower.json, I added (following the read components issue:
"overrides": {
"modernizr": {
"main": "modernizr.js"
}
}
and brunch build is running beautifully now.

C++ project organisation (with gtest, cmake and doxygen)

I am new to programming in general so I decided that I would start by making a simple vector class in C++. However I would like to get in to good habits from the start rather than trying to modify my workflow later on.
I currently have only two files vector3.hpp and vector3.cpp. This project will slowly start to grow (making it much more of a general linear algebra library) as I become more familiar with everything, so I would like to adopt a "standard" project layout to make life easier later on. So after looking around I have found two ways to go about organizing hpp and cpp files, the first being:
project
└── src
├── vector3.hpp
└── vector3.cpp
and the second being:
project
├── inc
│ └── project
│ └── vector3.hpp
└── src
└── vector3.cpp
Which would you recommend and why?
Secondly I would like to use the Google C++ Testing Framework for unit testing my code as it seems fairly easy to use. Do you suggest bundling this with my code, for example in a inc/gtest or contrib/gtest folder? If bundled, do you suggest using the fuse_gtest_files.py script to reduce the number or files, or leaving it as is? If not bundled how is this dependency handled?
When it comes to writing tests, how are these generally organized? I was thinking to have one cpp file for each class (test_vector3.cpp for example) but all compiled in to one binary so that they can all be run together easily?
Since the gtest library is generally build using cmake and make, I was thinking that it would make sense for my project to also be built like this? If I decided to use the following project layout:
├── CMakeLists.txt
├── contrib
│ └── gtest
│ ├── gtest-all.cc
│ └── gtest.h
├── docs
│ └── Doxyfile
├── inc
│ └── project
│ └── vector3.cpp
├── src
│ └── vector3.cpp
└── test
└── test_vector3.cpp
How would the CMakeLists.txt have to look so that it can either build just the library or the library and the tests? Also I have seen quite a few projects that have a build and a bin directory. Does the build happen in the build directory and then the binaries moved out in to the bin directory? Would the binaries for the tests and the library live in the same place? Or would it make more sense to structure it as follows:
test
├── bin
├── build
└── src
└── test_vector3.cpp
I would also like to use doxygen to document my code. Is it possible to get this to automatically run with cmake and make?
Sorry for so many questions, but I have not found a book on C++ that satisfactorily answers these type of questions.
C++ build systems are a bit of a black art and the older the project
the more weird stuff you can find so it is not surprising that a lot
of questions come up. I'll try to walk through the questions one by one and mention some general things regarding building C++ libraries.
Separating headers and cpp files in directories. This is only
essential if you are building a component that is supposed to be used
as a library as opposed to an actual application. Your headers are the
basis for users to interact with what you offer and must be
installed. This means they have to be in a subdirectory (no-one wants
lots of headers ending up in top-level /usr/include/) and your
headers must be able to include themselves with such a setup.
└── prj
├── include
│   └── prj
│   ├── header2.h
│   └── header.h
└── src
└── x.cpp
works well, because include paths work out and you can use easy
globbing for install targets.
Bundling dependencies: I think this largely depends on the ability of
the build system to locate and configure dependencies and how
dependent your code on a single version is. It also depends on how
able your users are and how easy is the dependency to install on their
platform. CMake comes with a find_package script for Google
Test. This makes things a lot easier. I would go with bundling only
when necessary and avoid it otherwise.
How to build: Avoid in-source builds. CMake makes out of source-builds
easy and it makes life a lot easier.
I suppose you also want to use CTest to run tests for your system (it
also comes with build-in support for GTest). An important decision for
directory layout and test organization will be: Do you end up with
subprojects? If so, you need some more work when setting up CMakeLists
and should split your subprojects into subdirectories, each with its
own include and src files. Maybe even their own doxygen runs and
outputs (combining multiple doxygen projects is possible, but not easy
or pretty).
You will end up with something like this:
└── prj
├── CMakeLists.txt <-- (1)
├── include
│   └── prj
│   ├── header2.hpp
│   └── header.hpp
├── src
│   ├── CMakeLists.txt <-- (2)
│   └── x.cpp
└── test
├── CMakeLists.txt <-- (3)
├── data
│   └── testdata.yyy
└── testcase.cpp
where
(1) configures dependencies, platform specifics and output paths
(2) configures the library you are going to build
(3) configures the test executables and test-cases
In case you have sub-components I would suggest adding another hierarchy and use the tree above for each sub-project. Then things get tricky, because you need to decide if sub-components search and configure their dependencies or if you do that in the top-level. This should be decided on a case-by-case basis.
Doxygen: After you managed to go through the configuration dance of
doxygen, it is trivial to use CMake add_custom_command to add a
doc target.
This is how my projects end up and I have seen some very similar projects, but of course this is no cure all.
Addendum At some point you will want to generate a config.hpp
file that contains a version define and maybe a define to some version
control identifier (a Git hash or SVN revision number). CMake has
modules to automate finding that information and to generate
files. You can use CMake's configure_file to replace variables in a
template file with variables defined inside the CMakeLists.txt.
If you are building libraries you will also need an export define to
get the difference between compilers right, e.g. __declspec on MSVC
and visibility attributes on GCC/clang.
As a starter, there are some conventional names for directories that you cannot ignore, these are based on the long tradition with the Unix file system. These are:
trunk
├── bin : for all executables (applications)
├── lib : for all other binaries (static and shared libraries (.so or .dll))
├── include : for all header files
├── src : for source files
└── doc : for documentation
It is probably a good idea to stick to this basic layout, at least at the top-level.
About splitting the header files and source files (cpp), both schemes are fairly common. However, I tend to prefer keeping them together, it is just more practical on day-to-day tasks to have the files together. Also, when all the code is under one top-level folder, i.e., the trunk/src/ folder, you can notice that all the other folders (bin, lib, include, doc, and maybe some test folder) at the top level, in addition to the "build" directory for an out-of-source build, are all folders that contain nothing more than files that are generated in the build process. And thus, only the src folder needs to be backed up, or much better, kept under a version control system / server (like Git or SVN).
And when it comes to installing your header files on the destination system (if you want to eventually distribute your library), well, CMake has a command for installing files (implicitly creates a "install" target, to do "make install") which you can use to put all the headers into the /usr/include/ directory. I just use the following cmake macro for this purpose:
# custom macro to register some headers as target for installation:
# setup_headers("/path/to/header/something.h" "/relative/install/path")
macro(setup_headers HEADER_FILES HEADER_PATH)
foreach(CURRENT_HEADER_FILE ${HEADER_FILES})
install(FILES "${SRCROOT}${CURRENT_HEADER_FILE}" DESTINATION "${INCLUDEROOT}${HEADER_PATH}")
endforeach(CURRENT_HEADER_FILE)
endmacro(setup_headers)
Where SRCROOT is a cmake variable that I set to the src folder, and INCLUDEROOT is cmake variable that I configure to wherever to headers need to go. Of course, there are many other ways to do this, and I'm sure my way is not the best. The point is, there is no reason to split the headers and sources just because only the headers need to be installed on the target system, because it is very easy, especially with CMake (or CPack), to pick out and configure the headers to be installed without having to have them in a separate directory. And this is what I have seen in most libraries.
Quote: Secondly I would like to use the Google C++ Testing Framework for unit testing my code as it seems fairly easy to use. Do you suggest bundling this with my code, for example in a "inc/gtest" or "contrib/gtest" folder? If bundled, do you suggest using the fuse_gtest_files.py script to reduce the number or files, or leaving it as is? If not bundled how is this dependency handled?
Don't bundle dependencies with your library. This is generally a pretty horrible idea, and I always hate it when I'm stuck trying to build a library that did that. It should be your last resort, and beware of the pitfalls. Often, people bundle dependencies with their library either because they target a terrible development environment (e.g., Windows), or because they only support an old (deprecated) version of the library (dependency) in question. The main pitfall is that your bundled dependency might clash with already installed versions of the same library / application (e.g., you bundled gtest, but the person trying to build your library already has a newer (or older) version of gtest already installed, then the two might clash and give that person a very nasty headache). So, as I said, do it at your own risk, and I would say only as a last resort. Asking the people to install a few dependencies before being able to compile your library is a much lesser evil than trying to resolve clashes between your bundled dependencies and existing installations.
Quote: When it comes to writing tests, how are these generally organised? I was thinking to have one cpp file for each class (test_vector3.cpp for example) but all compiled in to one binary so that they can all be run together easily?
One cpp file per class (or small cohesive group of classes and functions) is more usual and practical in my opinion. However, definitely, don't compile them all into one binary just so that "they can all be run together". That's a really bad idea. Generally, when it comes to coding, you want to split things up as much as it is reasonable to do so. In the case of unit-tests, you don't want one binary to run all the tests, because that means that any little change that you make to anything in your library is likely to cause a near total recompilation of that unit-test program, and that's just minutes / hours lost waiting for recompilation. Just stick to a simple scheme: 1 unit = 1 unit-test program. Then, use either a script or a unit-test framework (such as gtest and/or CTest) to run all the test programs and report to failure/success rates.
Quote: Since the gtest library is generally build using cmake and make, I was thinking that it would make sense for my project to also be built like this? If I decided to use the following project layout:
I would rather suggest this layout:
trunk
├── bin
├── lib
│ └── project
│ └── libvector3.so
│ └── libvector3.a products of installation / building
├── docs
│ └── Doxyfile
├── include
│ └── project
│ └── vector3.hpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── src
│ └── CMakeLists.txt
│ └── Doxyfile.in
│ └── project part of version-control / source-distribution
│ └── CMakeLists.txt
│ └── vector3.hpp
│ └── vector3.cpp
│ └── test
│ └── test_vector3.cpp
│_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
│
├── build
└── test working directories for building / testing
└── test_vector3
A few things to notice here. First, the sub-directories of your src directory should mirror the sub-directories of your include directory, this is just to keep things intuitive (also, try to keep your sub-directory structure reasonably flat (shallow), because deep nesting of folders is often more of a hassle than anything else). Second, the "include" directory is just an installation directory, its contents are just whatever headers are picked out of the src directory.
Third, the CMake system is intended to be distributed over the source sub-directories, not as one CMakeLists.txt file at the top-level. This keeps things local, and that's good (in the spirit of splitting things up into independent pieces). If you add a new source, a new header, or a new test program, all you need is to edit one small and simple CMakeLists.txt file in the sub-directory in question, without affecting anything else. This also allows you to restructure the directories with ease (CMakeLists are local and contained in the sub-directories being moved). The top-level CMakeLists should contain most of the top-level configurations, such as setting up destination directories, custom commands (or macros), and finding packages installed on the system. The lower-level CMakeLists should contain only simple lists of headers, sources, and unit-test sources, and the cmake commands that register them to compilation targets.
Quote: How would the CMakeLists.txt have to look so that it can either build just the library or the library and the tests?
Basic answer is that CMake allows you to specifically exclude certain targets from "all" (which is what is built when you type "make"), and you can also create specific bundles of targets. I can't do a CMake tutorial here, but it is fairly straight forward to find out by yourself. In this specific case, however, the recommended solution is, of course, to use CTest, which is just an additional set of commands that you can use in the CMakeLists files to register a number of targets (programs) that are marked as unit-tests. So, CMake will put all the tests in a special category of builds, and that is exactly what you asked for, so, problem solved.
Quote: Also I have seen quite a few projects that have a build ad a bin directory. Does the build happen in the build directory and then the binaries moved out in to the bin directory? Would the binaries for the tests and the library live in the same place? Or would it make more sense to structure it as follows:
Having a build directory outside the source ("out-of-source" build) is really the only sane thing to do, it is the de facto standard these days. So, definitely, have a separate "build" directory, outside the source directory, just as the CMake people recommend, and as every programmer I have ever met does. As for the bin directory, well, that is a convention, and it is probably a good idea to stick to it, as I said in the beginning of this post.
Quote: I would also like to use doxygen to document my code. Is it possible to get this to automatically run with cmake and make?
Yes. It is more than possible, it is awesome. Depending on how fancy you want to get, there are several possibilities. CMake does have a module for Doxygen (i.e., find_package(Doxygen)) which allows you to register targets that will run Doxygen on some files. If you want to do more fancy things, like updating the version number in the Doxyfile, or automatically entering a date / author stamps for source files and so on, it is all possible with a bit of CMake kung-fu. Generally, doing this will involve that you keep a source Doxyfile (e.g., the "Doxyfile.in" that I put in the folder layout above) which has tokens to be found and replaced by CMake's parsing commands. In my top-level CMakeLists file, you will find one such piece of CMake kung-fu that does a few fancy things with cmake-doxygen together.
Structuring the project
I would generally favour the following:
├── CMakeLists.txt
|
├── docs/
│ └── Doxyfile
|
├── include/
│ └── project/
│ └── vector3.hpp
|
├── src/
└── project/
└── vector3.cpp
└── test/
└── test_vector3.cpp
This means that you have a very clearly defined set of API files for your library, and the structure means that clients of your library would do
#include "project/vector3.hpp"
rather than the less explicit
#include "vector3.hpp"
I like the structure of the /src tree to match that of the /include tree, but that's personal preference really. However, if your project expands to contain subdirectories within /include/project, it would generally help to match those inside the /src tree.
For the tests, I favour keeping them "close" to the files they test, and if you do end up with subdirectories within /src, it's a pretty easy paradigm for others to follow if they want to find a given file's test code.
Testing
Secondly I would like to use the Google C++ Testing Framework for unit testing my code as it seems fairly easy to use.
Gtest is indeed simple to use and is fairly comprehensive in terms of its capabilities. It can be used alongside gmock very easily to extend its capabilities, but my own experiences with gmock have been less favourable. I'm quite prepared to accept that this may well be down to my own shortcomings, but gmock tests tends to be more difficult to create, and much more fragile / difficult to maintain. A big nail in the gmock coffin is that it really doesn't play nice with smart pointers.
This is a very trivial and subjective answer to a huge question (which probably doesn't really belong on S.O.)
Do you suggest bundling this with my code, for example in a "inc/gtest" or "contrib/gtest" folder? If bundled, do you suggest using the fuse_gtest_files.py script to reduce the number or files, or leaving it as is? If not bundled how is this dependency handled?
I prefer using CMake's ExternalProject_Add module. This avoids you having to keep gtest source code in your repository, or installing it anywhere. It is downloaded and built in your build tree automatically.
See my answer dealing with the specifics here.
When it comes to writing tests, how are these generally organised? I was thinking to have one cpp file for each class (test_vector3.cpp for example) but all compiled in to one binary so that they can all be run together easily?
Good plan.
Building
I'm a fan of CMake, but as with your test-related questions, S.O. is probably not the best place to ask for opinions on such a subjective issue.
How would the CMakeLists.txt have to look so that it can either build just the library or the library and the tests?
add_library(ProjectLibrary <All library sources and headers>)
add_executable(ProjectTest <All test files>)
target_link_libraries(ProjectTest ProjectLibrary)
The library will appear as a target "ProjectLibrary", and the test suite as a target "ProjectTest". By specifying the library as a dependency of the test exe, building the test exe will automatically cause the library to be rebuilt if it is out of date.
Also I have seen quite a few projects that have a build ad a bin directory. Does the build happen in the build directory and then the binaries moved out in to the bin directory? Would the binaries for the tests and the library live in the same place?
CMake recommends "out-of-source" builds, i.e. you create your own build directory outside the project and run CMake from there. This avoids "polluting" your source tree with build files, and is highly desirable if you're using a vcs.
You can specify that the binaries are moved or copied to a different directory once built, or that they are created by default in another directory, but there's generally no need. CMake provides comprehensive ways to install your project if desired, or make it easy for other CMake projects to "find" the relevant files of your project.
With regards to CMake's own support for finding and executing gtest tests, this would largely be inappropriate if you build gtest as part of your project. The FindGtest module is really designed to be used in the case where gtest has been built separately outside of your project.
CMake provides its own test framework (CTest), and ideally, every gtest case would be added as a CTest case.
However, the GTEST_ADD_TESTS macro provided by FindGtest to allow easy addition of gtest cases as individual ctest cases is somewhat lacking in that it doesn't work for gtest's macros other than TEST and TEST_F. Value- or Type-parameterised tests using TEST_P, TYPED_TEST_P, etc. aren't handled at all.
The problem doesn't have an easy solution that I know of. The most robust way to get a list of gtest cases is to execute the test exe with the flag --gtest_list_tests. However, this can only be done once the exe is built, so CMake can't make use of this. Which leaves you with two choices; CMake must try to parse C++ code to deduce the names of the tests (non-trivial in the extreme if you want to take into account all gtest macros, commented-out tests, disabled tests), or test cases are added by hand to the CMakeLists.txt file.
I would also like to use doxygen to document my code. Is it possible to get this to automatically run with cmake and make?
Yes - although I have no experience on this front. CMake provides FindDoxygen for this purpose.
In addition to the other (excellent) answers, I am going to describe a structure I've been using for relatively large-scale projects.
I am not going to address the subquestion about Doxygen, since I would just repeat what is said in the other answers.
Rationale
For modularity and maintainability, the project is organized as a set of small units.
For clarity, let's name them UnitX, with X = A, B, C, ... (but they can have any general name).
The directory structure is then organized to reflect this choice, with the possibility to group units if necessary.
Solution
The basic directory layout is the following (content of units is detailed later on):
project
├── CMakeLists.txt
├── UnitA
├── UnitB
├── GroupA
│ └── CMakeLists.txt
│ └── GroupB
│ └── CMakeLists.txt
│ └── UnitC
│ └── UnitD
│ └── UnitE
project/CMakeLists.txt could contain the following:
cmake_minimum_required(VERSION 3.0.2)
project(project)
enable_testing() # This will be necessary for testing (details below)
add_subdirectory(UnitA)
add_subdirectory(UnitB)
add_subdirectory(GroupA)
and project/GroupA/CMakeLists.txt:
add_subdirectory(GroupB)
add_subdirectory(UnitE)
and project/GroupB/CMakeLists.txt:
add_subdirectory(UnitC)
add_subdirectory(UnitD)
Now to the structure of the different units (let's take, as an example, UnitD)
project/GroupA/GroupB/UnitD
├── README.md
├── CMakeLists.txt
├── lib
│ └── CMakeLists.txt
│ └── UnitD
│ └── ClassA.h
│ └── ClassA.cpp
│ └── ClassB.h
│ └── ClassB.cpp
├── test
│ └── CMakeLists.txt
│ └── ClassATest.cpp
│ └── ClassBTest.cpp
│ └── [main.cpp]
To the different components:
I like having source (.cpp) and headers (.h) in the same folder. This avoids a duplicate directory hierarchy, makes maintenance easier. For installation, it is no problem (especially with CMake) to just filter the header files.
The role of the directory UnitD is to later on allow including files with #include <UnitD/ClassA.h>. Also, when installing this unit, you can just copy the directory structure as is. Note that you can also organize your source files in subdirectories.
I like a README file to summarize what the unit is about and specify useful information about it.
CMakeLists.txt could simply contain:
add_subdirectory(lib)
add_subdirectory(test)
lib/CMakeLists.txt:
project(UnitD)
set(headers
UnitD/ClassA.h
UnitD/ClassB.h
)
set(sources
UnitD/ClassA.cpp
UnitD/ClassB.cpp
)
add_library(${TARGET_NAME} STATIC ${headers} ${sources})
# INSTALL_INTERFACE: folder to which you will install a directory UnitD containing the headers
target_include_directories(UnitD
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
PUBLIC $<INSTALL_INTERFACE:include/SomeDir>
)
target_link_libraries(UnitD
PUBLIC UnitA
PRIVATE UnitC
)
Here, note that it is not necessary to tell CMake that we want the include directories for UnitA and UnitC, as this was already specified when configuring those units. Also, PUBLIC will tell all targets that depend on UnitD that they should automatically include the UnitA dependency, while UnitC won't be required then (PRIVATE).
test/CMakeLists.txt (see further below if you want to use GTest for it):
project(UnitDTests)
add_executable(UnitDTests
ClassATest.cpp
ClassBTest.cpp
[main.cpp]
)
target_link_libraries(UnitDTests
PUBLIC UnitD
)
add_test(
NAME UnitDTests
COMMAND UnitDTests
)
Using GoogleTest
For Google Test, the easiest is if its source is present in somewhere your source directory, but you don't have to actually add it there yourself.
I've been using this project to download it automatically, and I wrap its usage in a function to make sure that it is downloaded only once, even though we have several test targets.
This CMake function is the following:
function(import_gtest)
include (DownloadProject)
if (NOT TARGET gmock_main)
include(DownloadProject)
download_project(PROJ googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.0
UPDATE_DISCONNECTED 1
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) # Prevent GoogleTest from overriding our compiler/linker options when building with Visual Studio
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()
endfunction()
and then, when I want to use it inside one of my test targets, I will add the following lines to the CMakeLists.txt (this is for the example above, test/CMakeLists.txt):
import_gtest()
target_link_libraries(UnitDTests gtest_main gmock_main)