Using target_sources with INTERFACE assembly sources does not generate absolute paths.

Was the ASM language simply left out of 3.13’s target_sources relative path changes?
I currently have to use CMAKE_CURRENT_SOURCE_DIR when listing assembly files under INTERFACE sources.

I have the following project tree:

freertos
├── CMakeLists.txt # final static library target is created here if requested
└── portable
    ├── CMakeLists.txt # includes subdirectories for specific ports based on toolchain
    └── GCC # all ports for GCC toolchain variants
        └── ARM_CA9
            ├── CMakeLists.txt # defines sources for GCC_ARM_CA9 port as an interface library
            ├── port.c
            ├── portASM.S
            └── portmacro.h

When I configure this port with CMake 3.22 I end up with the following error:

CMake Error at freertos/CMakeLists.txt:209 (add_library):
  Cannot find source file:

    portASM.S

  Tried extensions .c .C .c++ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm .h
  .hh .h++ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90 .f95 .f03 .hip .ispc

My project has ASM language enabled.
If I remove portASM.S from target_sources, CMake configures just fine, but my project will not build without this assembly file.

Do you have the ASM language enabled in your project? Only C and CXX are enabled by default. If you haven’t explicitly enabled ASM, that might mean assembly source file extensions are not handled (that’s just a guess, I haven’t checked the code).

EDIT: Sorry, just saw you already mentioned that in your post.

Can you show the actual CMakeLists.txt file(s) involved in your project?

The full CMake project may be found at Paul Bartell’s FreeRTOS-Kernel fork.

Previously, ports were defined using generator expressions in portable’s list file. This required -DFREERTOS-PORT to be specified. For CI/CD, we want to be able to compile all targets a toolchain variant & version is able to without having to reconfigure between runs.

A project in the current branch would build with cmake -B build --toolchain freertos/toolchain/gcc-arm.cmake with a similar root-level list file:

###############################################################################
# CMakeLists.txt
###############################################################################
cmake_minimum_required(VERSION 3.16)
project(example C ASM)

add_library(freertos_config INTERFACE)
target_sources(freertos_config INTERFACE FreeRTOSConfig.h)
target_include_directories(freertos_config SYSTEM INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(freertos_config INTERFACE projCOVERAGE_TEST=0)

set(FREERTOS_HEAP "4" CACHE STRING "FreeRTOS heap model number. 1 .. 5. Or absolute path to custom heap source file")
set(FREERTOS_PORT "GCC_ARM_CA9" CACHE STRING "FreeRTOS Port Identifier")

# should really be using fetch content with a paritcular release tag
# but this list file was created for local testing.
add_subdirectory(freertos)

target_compile_options(freertos_kernel
    PUBLIC
        -mthumb
        -mcpu=cortex-a9
        -mfpu=auto
        -mfloat-abi=hard )

###############################################################################
# FreeRTOS Kernel
# Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
# SPDX-License-Identifier: MIT
###############################################################################

###############################################################################
# FreeRTOS/CMakeLists.txt
###############################################################################
cmake_minimum_required(VERSION 3.16)
project(FreeRTOS-Kernel C ASM)

# detect toolchain being used

add_subdirectory( portable )

add_library( freertos_kernel STATIC )

# setup freertos_kernel_core and freertos_heap targets
# user supplies freertos_config target

target_link_libraries( freertos_kernel
    PUBLIC
        freertos_config
        freertos_heap
        # Aliased to the target FreeRTOS:Kernel:Port::${FREERTOS_PORT} is aliased to.
        freertos_kernel_port
        freertos_kernel_core )
###############################################################################
# FreeRTOS/portable/CMakeLists.txt
###############################################################################

###############################################################################
# Helper for defining a FreeRTOS port
# Useful for simple ports
###############################################################################
macro( freertos_define_port )
    set(oneValueArgs
        NAME )    # name to give to port library

    set(multiValueArgs
        ALIAS     # backwards-compatible name(s)
        HEADERS   # header files this port uses
        SOURCES ) # source files this port uses

    cmake_parse_arguments(PORT
        ""
        "${oneValueArgs}"
        "${multiValueArgs}"
        ${ARGN} )

    add_library( ${PORT_NAME} INTERFACE )

    target_sources( ${PORT_NAME} INTERFACE "${PORT_SOURCES}" )

    if( ${CMAKE_VERSION} VERSION_LESS "3.23.0" )
        target_sources( ${PORT_NAME} PUBLIC "${PORT_HEADERS}" )
        target_include_directories( ${PORT_NAME} INTERFACE . )
    else()
        target_sources( ${PORT_NAME}
            PUBLIC
                FILE_SET HEADERS
                    BASE_DIRS .
                    FILES "${PORT_HEADERS}" )
    endif()

    foreach( ALIAS IN LISTS PORT_ALIAS )
        add_library( FreeRTOS::Kernel::Port::${ALIAS} ALIAS ${PORT_NAME} )
    endforeach()
endmacro()


# while handling specific port names for backwards compatibility:
    add_subdirectory( GCC/ARM_CA9 )

    get_target_property(target_name
        FreeRTOS::Kernel::Port::${FREERTOS_PORT}
        ALIASED_TARGET )
    add_library(freertos_kernel_port ALIAS ${target_name})

There are dozens of ports we are building with CMake, this is just one of them, hence the macro in portable’s list file. Several of them have various file extensions for ASM, some use inline ASM in a C file which also works just fine.


###############################################################################
# FreeRTOS/portable/GCC/ARM_CA9/CMakeLists.txt ARM CA9 for GCC
###############################################################################
freertos_define_port(
    NAME freertos_port_arm_ca9
    ALIAS GCC_ARM_CA9
    HEADERS
        portmacro.h
    SOURCES
        port.c
        portASM.S )

I don’t see anything specific to any particular language in the change that added that behavior. I suggest you open an issue in CMake’s issue tracker. You would be well advised to add a minimal project which demonstrates the problem, as linking to a large, complicated project like yours will make it much less likely for anyone to want to investigate it.

While reproducing, everything was fine up until creating a similar macro in a parent directory’s list file.

I found the exact problem:
quote-wrapping a dereferenced list passes it semi-colon delimited, while I believe the 3.13 change assumes that the list is merely dereferenced which passes each source space-delimited, as its own variable

macro (my_add_library)
    message(STATUS "Macro invoked from ${CMAKE_CURRENT_SOURCE_DIR}")
    set(singleValueArgs "NAME")
    set(multiValueArgs "SOURCES;ASM")
    cmake_parse_arguments(MY "" "${singleValueArgs}" "${multiValueArgs}" ${ARGN})

    message(STATUS "Adding ${MY_NAME} library with sources: ${MY_SOURCES}")
    add_library(${MY_NAME} INTERFACE)

    target_sources( ${MY_NAME} INTERFACE "${MY_SOURCES}" )

    foreach(SOURCE IN LISTS MY_SOURCES MY_ASM)
        find_file(FOUND_${SOURCE} ${SOURCE} PATHS .)
        message(STATUS "${SOURCE}: '${FOUND_${SOURCE}}'")
    endforeach()

    get_target_property(MY_INTERFACE_SOURCES ${MY_NAME} INTERFACE_SOURCES)
    message(STATUS "Added sources: ${MY_INTERFACE_SOURCES}")
endmacro()

# defer creation of inner library to path
add_subdirectory(path)
cmake -B build
-- Macro invoked from /home/cookpate/sandbox/cmakebug/relative/path
-- Adding inner library with sources: main.c;emptyasm.s
-- main.c: '/home/cookpate/sandbox/cmakebug/relative/path/main.c'
-- emptyasm.s: '/home/cookpate/sandbox/cmakebug/relative/path/emptyasm.s'
-- Added sources: /home/cookpate/sandbox/cmakebug/relative/path/main.c;emptyasm.s
-- Configuring done
CMake Error at CMakeLists.txt:7 (add_library):
  Cannot find source file:

    emptyasm.s

  Tried extensions .c .C .c++ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm .h
  .hh .h++ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90 .f95 .f03 .hip .ispc


CMake Error at CMakeLists.txt:7 (add_library):
  No SOURCES given to target: outer

unquoting the list gives each value in INTERFACE_SOURCES an absolute path. This doesn’t depend on source languages used.

target_sources( ${MY_NAME} INTERFACE "${MY_SOURCES}" )

That line is incorrect. The ${MY_SOURCES} should not be quoted. The target_sources() command expects each source file as a separate argument. If MY_SOURCES contained more than one file, target_sources() would end up seeing a single file with a semicolon in its file name, not two separate file names. I think what you’re actually seeing is target_sources() sees main.c;emptyasm.s, interprets it as a relative path and prepends ${CMAKE_CURRENT_SOURCE_DIR} to that whole string. From it’s perspective, there is a single file whose name is main.c;emptyasm.s. Then, later on during the generation stage, the semicolon is interpreted as a list separator, so at that point, emptyasm.s is now seen as a separate file.

TLDR: Don’t quote a whole list of files when passing it to target_sources(). Doing so makes it see that as a single file name when deciding whether to prepend a path or not.