# Migrating a configurable autotools-based build to CMake

**URL:** <https://discourse.cmake.org/t/migrating-a-configurable-autotools-based-build-to-cmake/5784>\
**Category:** Code\
**Created:** [June 3, 2022, 3:50am UTC](https://discourse.cmake.org/t/migrating-a-configurable-autotools-based-build-to-cmake/5784 "2022-06-03T03:50:14Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![vkn](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/v/fbc32d/32.png) [@vkn](https://discourse.cmake.org/u/vkn)\
**Post date:** [June 3, 2022, 3:50am UTC](https://discourse.cmake.org/t/migrating-a-configurable-autotools-based-build-to-cmake/5784/1 "2022-06-03T03:50:14Z")

</div>

I originally posted this on StackOverflow, I figured it would be helpful to post it here, too.

I am migrating a large legacy codebase from autotools to CMake, and we have some behaviour that we’d like to preserve. However, my naive initial reimplementation of the build system in CMake has resulted in the entire project being rebuilt every time cmake is invoked. I believe this is happening because of the way that `include_directories` is being used, along with a lack of cache variables, but I’d like to double-check here if a better solution exists than the one I’m thinking of.

The original reimplementation was done in CMake 2.8 (corporate slowness, hooray), but I’ve managed to get hold of 3.14, so I’ve tried to tidy it up a little. I’d like to use some more modern features if possible.

I should also add that it would be very preferable to not rely on things like environment variables. Ultimately, we would like to have Visual Studio as an option available to developers when working on this, as the project itsellf builds on Linux, and VS has the capability to understand CMake projects and build remotely on a Linux box.

* * *

## Legacy Autotools setup

`conf.sh`:

```auto
#!/bin/sh
DIR_=`dirname $0`
${DIR_}/configure \
  --some-flag=yes \
  --some-other-flag=ok \
  --some-dependency=/home/deps/dependencyOne \
  --some-other-dependency=/home/deps/dependencyTwo \
"$@"

exit %?

```

configure.in contains the decoding logic to set these dependencies up:

```auto
AC_ARG_ENABLE(some-dependency),
[
if test "x$some_dependency" != "xno" ; then
  if test -d "${enableval}"; then
    DEPONE_DIR=$enableval
  else
    AC_MSG_ERROR("Must specify dependencyOne directory!")
  fi
  DEPONE_INCLUDES="-I$DEPONE_DIR/include"
  DEPONE_LDFLAGS="-L$DEPONE_DIR/lib -ldepone"

  #etcetera
fi
]

```

Running `./conf.sh` from the desired `build` directory generates the Makefiles and such. After this, we run `make`. If we change a source file, it only rebuilds that source and its dependees (dependors? Is there a word for this). If we change a Makefile.am, again, it’s smart enough to not rebuild everything.

* * *

## Current CMakeLists.txt setup:

We want to be able to switch dependencies on and off in the build, and the way we do this is through this configure script. I originally recreated the existing setup closely in CMake as shown below:

Config script:

```auto
#!/bin/bash
libs=(
  -Dsome-dependency=/home/deps/dependencyOne
  -Dsome-other-dependency=/home/deps/dependencyTwo
)

args=(
  ${libs[@]}
  -Dsome-flag=yes
  -Dsome-other-flag=ok
)

cmake "${args[@]}"

```

Root CMakeLists.txt:

```auto
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
PROJECT(myProject)

#some language-specific setup (standard etc)

if(some-dependency)
  link_directories(${some-dependency}/lib)
  include_directories(${some-dependency}/include)
endif()

if(some-other-dependency)
  link_directories(${some-other-dependency}/lib)
  include_directories(${some-other-dependency}/include)
endif()

```

I noticed that if I switch off a line in the config script, running cmake will have cached the value and still act as if it was enabled, which I don’t want. So I committed this cardinal sin:

End of root CMakeLists.txt:

```auto
unset(some-dependency CACHE)
unset(some-other-dependency CACHE)

```

Now, every time I run the configure script (and by extension `cmake`), it rebuilds _everything_ when I run `make`. My guess is that this happens because of `include_directories` being reset every time, resulting in everything being marked as dirty.

My supposed solution to this is twofold:

- Use `target_include_directories`
- Check if the value supplied from the config script via -D matches an existing CACHE entry, and if so, do nothing. I don’t think this will work, given -D sets a variable in cache.

Is there a better way of getting this done? I get the impression that my attempt at it is fairly naive and simplistic, and if a better solution exists, I’d really love to know about it. I bought “Professional CMake” by Craig Scott, but couldn’t glean enough to see a straightforward solution to this.

---

<div class="post-metadata">

**Author:** ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)\
**Post date:** [June 6, 2022, 8:15pm UTC](https://discourse.cmake.org/t/migrating-a-configurable-autotools-based-build-to-cmake/5784/2 "2022-06-06T20:15:03Z")

</div>

> [@vkn](#):
>
> My guess is that this happens because of `include_directories` being reset every time, resulting in everything being marked as dirty.

Possible? If the values are made the same each time, it should no-op correctly. You can use `make -d` to have it say why it thinks each thing needs to rerun. If you use the `Ninja` generator `ninja -d explain` does it more concisely.

> [@vkn](#):
>
> ```auto
> unset(some-dependency CACHE)
> unset(some-other-dependency CACHE)
> 
> ```

You can instead do something like:

```cmake
if (NOT current_value STREQUAL previous_value)
  unset(current_value CACHE)
endif ()
set(previous_value "${current_value}" CACHE INTERNAL "")

```

to instead only remove the cache variable when its value changes. But I doubt that _this_ is the actual problem here. I would instead suspect a generated file that is included at some core is written manually using `file(WRITE)` instead of using something like `configure_file`.

---

<div class="post-metadata">

**Author:** ![ben.boeckel](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/b/ea5d25/32.png) [@ben.boeckel](https://discourse.cmake.org/u/ben.boeckel)\
**Post date:** [June 6, 2022, 8:16pm UTC](https://discourse.cmake.org/t/migrating-a-configurable-autotools-based-build-to-cmake/5784/3 "2022-06-06T20:16:04Z")

</div>

Oh, you want the absence of a flag to act as if it is the default. There’s no easy way to do that without `unset(CACHE)` AFAIK.

---

<div class="post-metadata">

**Author:** ![fdk17](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/f/ea666f/32.png) [@fdk17](https://discourse.cmake.org/u/fdk17)\
**Post date:** [June 6, 2022, 10:50pm UTC](https://discourse.cmake.org/t/migrating-a-configurable-autotools-based-build-to-cmake/5784/4 "2022-06-06T22:50:10Z")

</div>

I think you only need to do the first part of proposed solution: switch to using `target_include_directories`.

If I remember correctly using `include_directories()` will cause all the Makefiles to change when running the configure script; this causes all the targets to rebuild because its Makefile gets updated.

---

<div class="post-metadata">

**Author:** ![vkn](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/v/fbc32d/32.png) [@vkn](https://discourse.cmake.org/u/vkn)\
**Post date:** [June 7, 2022, 12:01am UTC](https://discourse.cmake.org/t/migrating-a-configurable-autotools-based-build-to-cmake/5784/5 "2022-06-07T00:01:06Z")

</div>

Thanks for the replies all.

I’ve decided to go with the following for now:

Root CMakeLists.txt:

```auto
...
set(dependencyOne /home/deps/dependencyOne CACHE PATH "blah")
option(enableDepOne "Enable DependencyOne" OFF)

if(enableDepOne)
  set(depOneIncludes ${dependencyOne}/include CACHE PATH " ")
  ...etc
endif()
...

```

Various other CMakeLists.txt:

```auto
add_library(some-library STATIC someSource.cpp)
target_include_directories(some-library PUBLIC ${depOneIncludes})

```

Then invoking cmake with:

```auto
cmake -DenableDepOne=ON

```

The invocation command will be in a separate bash script, and having explicitly commented-out or OFF hardly makes a difference. Having to go to the CMakeLists.txt to check for file paths in use is a little annoying, but this is the best solution I’ve managed to come up with so far.  
As fdk17 says, `target_include_directories` will avoid touching every single Makefile, and only those that are relevant as anything changes.  
I will investigate Ben’s suggestion that file(WRITE) is causing some issue (I am using that). `configure_file` seems to be the obvious better choice for this.
