diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5efc8d45..2840ff78 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -281,27 +281,6 @@ endif()
 
 add_subdirectory(src)
 
-# =====================================
-# Setup default port names
-# =====================================
-
-if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
-    set(DEFAULT_PAR_PORT "/dev/parport0")
-    set(DEFAULT_SER_PORT "/dev/ttyS0")
-elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
-    set(DEFAULT_PAR_PORT "/dev/ppi0")
-    set(DEFAULT_SER_PORT "/dev/cuad0")
-elseif (CMAKE_SYSTEM_NAME STREQUAL "Solaris")
-    set(DEFAULT_PAR_PORT "/dev/printers/0")
-    set(DEFAULT_SER_PORT "/dev/term/a")
-elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
-    set(DEFAULT_PAR_PORT "lpt1")
-    set(DEFAULT_SER_PORT "com1")
-else()
-    set(DEFAULT_PAR_PORT "unknown")
-    set(DEFAULT_SER_PORT "unknown")
-endif()
-
 # =====================================
 # Configuration
 # =====================================
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2cb8f5f1..00d67892 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -81,31 +81,53 @@ else()
     add_compile_options(-Wall) # -Wextra
 endif()
 
+# =====================================
+# Setup default port names
+# =====================================
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    set(DEFAULT_PAR_PORT "/dev/parport0")
+    set(DEFAULT_SER_PORT "/dev/ttyS0")
+elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+    set(DEFAULT_PAR_PORT "/dev/ppi0")
+    set(DEFAULT_SER_PORT "/dev/cuad0")
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Solaris")
+    set(DEFAULT_PAR_PORT "/dev/printers/0")
+    set(DEFAULT_SER_PORT "/dev/term/a")
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
+    set(DEFAULT_PAR_PORT "lpt1")
+    set(DEFAULT_SER_PORT "com1")
+else()
+    set(DEFAULT_PAR_PORT "unknown")
+    set(DEFAULT_SER_PORT "unknown")
+endif()
+
 # =====================================
 # Configure files
 # =====================================
 
-macro(configure_option option)
-    if(${${option}})
-        string(REGEX REPLACE "(.*)@${option}_BEGIN@(.*)@${option}_END@(.*)" "\\1\\2\\3" conf_file "${conf_file}")
-    else()
-        string(REGEX REPLACE "(.*)@${option}_BEGIN@(.*)@${option}_END@(.*)" "\\1\\3" conf_file "${conf_file}")
-    endif()
-endmacro()
-
-file(READ avrdude.conf.in conf_file)
-configure_option(HAVE_PARPORT)
-configure_option(HAVE_LINUXGPIO)
-configure_option(HAVE_LINUXSPI)
-file(WRITE "${PROJECT_BINARY_DIR}/avrdude.conf.in" "${conf_file}")
-
 configure_file(cmake_config.h.in ac_cfg.h)
-configure_file("${PROJECT_BINARY_DIR}/avrdude.conf.in" avrdude.conf)
 configure_file(avrdude.spec.in avrdude.spec)
 if(WIN32)
     configure_file(windows.rc.in windows.rc)
 endif()
 
+add_custom_command(
+    OUTPUT avrdude.conf
+    COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/avrdude.conf.in" avrdude.conf.in
+    COMMAND ${CMAKE_COMMAND}
+        -D HAVE_PARPORT=${HAVE_PARPORT}
+        -D HAVE_LINUXSPI=${HAVE_LINUXSPI}
+        -D HAVE_LINUXGPIO=${HAVE_LINUXGPIO}
+        -D DEFAULT_PAR_PORT=${DEFAULT_PAR_PORT}
+        -D DEFAULT_SER_PORT=${DEFAULT_SER_PORT}
+        -P "${CMAKE_CURRENT_SOURCE_DIR}/configure.cmake"
+    DEPENDS avrdude.conf.in
+    VERBATIM
+    )
+
+add_custom_target(conf ALL DEPENDS avrdude.conf)
+
 # =====================================
 # Project
 # =====================================
diff --git a/src/configure.cmake b/src/configure.cmake
new file mode 100644
index 00000000..938c1752
--- /dev/null
+++ b/src/configure.cmake
@@ -0,0 +1,35 @@
+#
+# configure.cmake - autoconf like multi-line configure
+# Copyright (C) 2022 Marius Greuel
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Do a multi-line replace based on @<OPTION>_BEGIN@ and @<OPTION>_END@ tags.
+macro(configure_option option)
+    if(${${option}})
+        string(REGEX REPLACE "(.*)@${option}_BEGIN@(.*)@${option}_END@(.*)" "\\1\\2\\3" CONTENTS "${CONTENTS}")
+    else()
+        string(REGEX REPLACE "(.*)@${option}_BEGIN@(.*)@${option}_END@(.*)" "\\1\\3" CONTENTS "${CONTENTS}")
+    endif()
+endmacro()
+
+# Perform autoconf like multi-line configure
+file(READ avrdude.conf.in CONTENTS)
+configure_option(HAVE_PARPORT)
+configure_option(HAVE_LINUXSPI)
+configure_option(HAVE_LINUXGPIO)
+file(WRITE avrdude.conf.in "${CONTENTS}")
+
+configure_file(avrdude.conf.in avrdude.conf)