1

I am writing a proof of concept project using Google protobuf-c-rpc.

I want to use CMake for building the project. I am using CMake ver 3.19

Here is my directory structure:

├── build
│   ├── CMakeCache.txt
│   └── CMakeFiles
├── CMakeLists.txt
├── interfaces
|------ example.proto
└── src
    ├── client
    ├───── foo_client.py
    └── server
      |-- include
      |── example-server.c

I have built and installed protobuf-c-rpc and it's dependencies locally

I want to use CMake to build as follows:

  1. Glob the interfaces folder and compile all *.proto files
  2. Generate C headers and source files from the *.proto files and place them in ${PROJECT_SOURCE_DIR}/server/include and ${PROJECT_SOURCE_DIR}/server/ respectively
  3. Generate python code from the *.proto files and place them in ${PROJECT_SOURCE_DIR}/client/core
  4. Compile the C files generated (including example-server.c)
  5. Link the object files from step 4 with the protobuff-c libs (currently, I have only managed to build the static libs for protobuf-c*)

This is my CMakeLists.txt file so far:

cmake_minimum_required(VERSION 3.10)

# set the project name
project(MyProj VERSION 0.10)

file(GLOB PROTOBUF_DEFINITION_FILES "interfaces/*.proto")
set(PROTOBUF_INPUT_DIRECTORY "${PROJECT_SOURCE_DIR}")
set(PROTOBUF_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/src/")
foreach(file ${PROTOBUF_DEFINITION_FILES})
    # Generate C stubs
    set(PROTOBUF_C_ARGUMENTS "protoc-c --proto_path=\"${PROTOBUF_INPUT_DIRECTORY}\" --c_out=\"${PROTOBUF_OUTPUT_DIRECTORY}\server\" \"${file}\"")
    execute_process(COMMAND ${PROTOBUF_C_ARGUMENTS}
            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
            RESULT_VARIABLE PROTOBUF_C_RESULT
            OUTPUT_VARIABLE PROTOBUF_C_OUTPUT_VARIABLE)

    # Generate Python bindings
    set(PROTOBUF_ARGUMENTS "protoc --proto_path=\"${PROTOBUF_INPUT_DIRECTORY}\" --python_out=\"${PROTOBUF_OUTPUT_DIRECTORY}\client\" \"${file}\"")
    execute_process(COMMAND ${PROTOBUF_ARGUMENTS}
            WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
            RESULT_VARIABLE PROTOBUF_RESULT
            OUTPUT_VARIABLE PROTOBUF_OUTPUT_VARIABLE)            
endforeach()

# Move C headers to include folder
file(GLOB GENERATED_C_HEADERS
  "${PROTOBUF_OUTPUT_DIRECTORY}/server/*.pb-c.h"
)
file(COPY ${GENERATED_C_HEADERS} DESTINATION "${PROTOBUF_OUTPUT_DIRECTORY}/server/include/")
# file(REMOVE "${GENERATED_C_HEADERS}" ) doesn't work? why can't I use variable here?
file(REMOVE "${PROTOBUF_OUTPUT_DIRECTORY}/server/*.pb-c.h")

file(GLOB PROTOBUF_MODELS_INCLUDES "src/server/*.pb-c.c" "src/server/includes/*.h")

# Need to build (compile + link) my custom C sources + generated C stubs + link to protobuf-c-* libraries
# ... ?

How do I modify the CMakeLists.txt above, to achieve the workflow I described above?

[[Edit]]

It would be great if I could also specify in the CMakeLists options for debug and release versions of the C executable

Homunculus Reticulli
  • 65,167
  • 81
  • 216
  • 341
  • 1
    "(currently, I have only managed to build the static libs for protobuf-c*)" - So the code you show us does everything except linking? I don't get your problem. BTW, the command specification `COMMAND ${PROTOBUF_OUTPUT_DIRECTORY}` seems to be wrong. Shouldn't it be `COMMAND ${PROTOBUF_ARGUMENTS}` instead? – Tsyvarev Oct 07 '21 at 15:48
  • Requirements 2, 3, 4 and 5 are not being satisfied by existing CMakeLists.txt file (files not being deposited in correct folders) - I suppose I could add separate line to generate the C and Python files ... I'm looking into your query about `${PROTOBUF_ARGUMENTS}` – Homunculus Reticulli Oct 07 '21 at 15:56
  • Have you tried to achieve requirement 2 (generating C files from `.proto`) in the **command line**? Knowing successful command line could help in constructing arguments for `execute_process`. – Tsyvarev Oct 07 '21 at 16:05
  • @Tsyvarev See edits made to the CMakeLists.txt file in my question – Homunculus Reticulli Oct 07 '21 at 16:48
  • For requirement 4 you need to enumerate all files you want to compile in `add_executable` or `add_library` call. (The first one creates an executable, the second one - a library). For collect files you could use `file(GLOB)` as usual. For link with Protobuf see e.g [that question](https://stackoverflow.com/questions/10010398/how-to-link-google-protobuf-libraries-via-cmake-on-linux). – Tsyvarev Oct 07 '21 at 16:49
  • `execute_process(COMMAND` ? No, use `add_custom_command`. `set(PROTOBUF_ARGUMENTS "protoc --proto_path=\"` Why the `"\"\"` escapes? Just `COMMAND stuff --part="stuff"`, like you would in shell. – KamilCuk Oct 07 '21 at 17:51
  • `file(REMOVE "${GENERATED_C_HEADERS}" ) doesn't work?` you quoted it, so it's one element. Don't quote it. | Do you know what files will be generated for each proto file? Is there a naming scheme? Is there a specific reason you generate files at configure stage, not at build stage? – KamilCuk Oct 07 '21 at 18:13
  • @KamilCuk: When I originally had it unquoted, a warning was issued by CMake: `CMake Error at CMakeLists.txt:33 (file): file must be called with at least two arguments.` C headers are named `*.pb.h`. Last, but not least, regarding the quoting, I have to escape the `"` in case file name contains spaces". Note that the command string itself is also quoted. – Homunculus Reticulli Oct 07 '21 at 18:23
  • `file name contains spaces` CMAke has _very_ confusing quoting rules, anyway you want `COMMAND protoc-c "--proto_path=${PROTOBUF_INPUT_DIRECTORY}" "--python_out=${PROTOBUF_OUTPUT_DIRECTORY}\client" "${file}"`. Quote the whole part to be quoted, not part of it. Don't `--abc="def"` do `"--abc=def"`. [for reference](https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#quoted-argument) `file must be called with at least two arguments` must be that `GENERATED_C_HEADERS` is empty. | `C headers are named *.pb.h` So a proto file named `abc.proto` will generate a file named `abc.pb.h`? – KamilCuk Oct 07 '21 at 21:30
  • Yes, I found out I had to split the command out like you mentioned (still not working - but I think this is because `execute_process` doesn't play nicely with `foreach()`. Last, but not least, an IDL file named abc.proto will generate a header named abc.pb-c.h (I made a mistake - but that is a red herring, ignore that). – Homunculus Reticulli Oct 07 '21 at 21:35
  • Any reason why aren't you using https://cmake.org/cmake/help/latest/module/FindProtobuf.html ? – KamilCuk Oct 07 '21 at 21:54
  • I am using the C binding, not the C++ binding - so I'm going to simply include the headers and static libs manually. At the moment, my main problem is actually iterating over the `.proto` files and generating the source files. When I enter the commands at the CLI, the files are generated, but in the `foreach()` loop, I get the error, "Directory does not exist". Once I can generate the files, I can sort out the rest. – Homunculus Reticulli Oct 07 '21 at 21:58

1 Answers1

0

Do not do it at CMake configuration stage. Do it at build stage.

CMake is a build system generator, build systems neede dependencies. Model dependencies. This file depends on that file and this command.

Here is a short script that should maybe get you started. I have written the script here, it may be full of errors and typos:

# Glob the interfaces folder 
file(GLOB files interfaces/*.proto)

set(pythonouts) # list for python output files
set(srcs)  # list for c/h generated sources
foreach(file IN LISTS files)
   get_filename_component(out ${file} NAME_WE)

   # Generate C headers and source files from the *.proto files and place 
   # them in ${PROJECT_SOURCE_DIR}/server/include and 
   # ${PROJECT_SOURCE_DIR}/server/ respectively
   add_custom_command(
       COMMENT "Generate this and that from ${file} using proto-c"
       COMMAND protoc-c
             --proto_path=${PROTOBUF_INPUT_DIRECTORY}
             --c_out=${PROTOBUF_OUTPUT_DIRECTORY}/server/
             ${file}
       COMMAND ${CMAKE_COMMAND} -E copy
            ${PROTOBUF_OUTPUT_DIRECTORY}/server/${out}.pb-c.h
            ${PROTOBUF_OUTPUT_DIRECTORY}/server/include/${out}.pb-c.h
       COMMAND ${CMAKE_COMMAND} -E remove
            ${PROTOBUF_OUTPUT_DIRECTORY}/server/${out}.pb-c.h
       DEPENDS
            ${file}
       OUTPUT
            ${PROTOBUF_OUTPUT_DIRECTORY}\server\${out}.pb-c.c
            ${PROTOBUF_OUTPUT_DIRECTORY}\server\include\${out}.pb-c.h
       VERBATIM
   )
   list(APPEND srcs
      ${PROTOBUF_OUTPUT_DIRECTORY}\server\include\${out}.pb-c.h
      ${PROTOBUF_OUTPUT_DIRECTORY}\server\${out}.pb-c.c
   )

   # Generate python code from the *.proto files and place
   # them in ${PROJECT_SOURCE_DIR}/client/core
   add_custom_command(
      COMMENT "Generate python this and that from ${file} using proto"
      COMMAND proto ....
      DEPENDS ${file}
      OUTPUT
         ${PROTOBUF_OUTPUT_DIRECTORY}\server\${out}.pb.python
      VERBATIM
   )
   list(APPEND pythonouts
       ${PROTOBUF_OUTPUT_DIRECTORY}\server\${out}.pb.python
   )

endfor()

add_custom_command(gen-python-files ALL)
depends(gen-python-files ${pythonouts})

# Compile the C files generated (including example-server.c)
add_executable(someexe example-server.c ${srcs})

# Link the object files from step 4 with the protobuff-c libs (currently, I have only managed to build the static libs for protobuf-c*)
# https://cmake.org/cmake/help/latest/module/FindProtobuf.html
find_package(Protobuf REQUIRED)
target_link_libraries(someexe PUHBLIC ${Protobuf_LIBRARIES})

And you probably would want to take insipiration from cmake findprotobuf if you intent to do it yourself.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Which version of CMake are you using? I am getting the error: `Unknown CMake command "for".` – Homunculus Reticulli Oct 07 '21 at 22:07
  • Should be `foreach`. – KamilCuk Oct 07 '21 at 22:14
  • 1
    There is no `move` command either I have had to use `copy` and will now try to use `remove` to see if I can replicate move. Here is the error message: `CMake Error: cmake version 3.19.6 Usage: cmake -E [arguments...]` – Homunculus Reticulli Oct 07 '21 at 23:06
  • I've managed to patch up most of the code you provided in your answer, to get (most) things to work at my end - could you please add some comments to your CMakeLists.txt file, so that it is a lot more clear what is going on, before I accept the answer? Thank you – Homunculus Reticulli Oct 08 '21 at 09:35
  • Python files are **NOT** being generated (I modified the stub line `COMMAND proto ....` and used the correct program `protoc` and correct arguments). – Homunculus Reticulli Oct 08 '21 at 09:46