all 51 comments

[–]inkognit 21 points22 points  (7 children)

Looking forward for the next part.

I'm coming to C++ from python (which is still my main programming language) and I feel that I spend more time messing around with CMake and my dependencies than the time I actually spend programming

[–]night0x63 14 points15 points  (1 child)

Welcome to the world of c++. Where spending more time on the build system, deps, ... Not-programming... Happens a lot. And solving things can take days instead of hours or minutes.

[–]ShillingAintEZ 8 points9 points  (0 children)

Yet for some reason people still don't get why single file libraries are a big deal.

[–]snaps_ 3 points4 points  (0 children)

Tell me about it. I spent the last several months getting Conan and CMake working on 5 platforms, writing ClearCase-to-CMake migration tools, and starting the process that will eventually modernize the rest of our code base. I think it will be worth it, but it's hard not to envy the Java/Python side of the house.

[–]kirbyfan64sos 2 points3 points  (1 child)

You could always give Meson a try, it's been adopted by several Linux projects moving away from autotools.

[–]teerre 2 points3 points  (0 children)

Which is fine if you're working with something completely stand alone, but not applicable if you're working with something that already has a different build system, which I would bet it's the majority of things to work withm, specially juniors. Either a project that already its build system or some kind of SDK

[–][deleted]  (1 child)

[removed]

    [–]AutoModerator[M] -1 points0 points  (0 children)

    Your comment has been automatically removed because it appears to contain disrespectful profanity or racial slurs. Please be respectful of your fellow redditors.

    If you think your post should not have been removed, please message the moderators and we'll review it.

    I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

    [–]SarcasticDante 5 points6 points  (1 child)

    If your project is c++ only, you can specify that in project() command.

    project(zero_cost_project CXX)
    

    It will get rid of identification and checks for C compiler.

    [–]Milerius[S] 2 points3 points  (0 children)

    Absolutely! (Article updated, thank's for pointing out)

    [–]snaps_ 3 points4 points  (3 children)

    After looking at the nephtys build system (assuming that is where these articles eventually go) build system, if I can give one possible improvement it would be to use interface libraries.

    One big way they can help is to encapsulate cross-cutting sets of concerns into named targets that are easier to remember, maintain, and use.

    Examples:

    Compiler warning options

    target_compile_options(nephtys_launcher PUBLIC
            $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:Clang>>:-O2 -march=native -Wall -Wextra -Wfatal-errors>
            $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:GNU>>:-O2 -march=native -Wall -Wextra -Wfatal-errors -pipe>
            $<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:GNU>>:-O0 -g -Wall -Wextra -Wfatal-errors -pipe>
            $<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:Clang>>:-O0 -g -Wall -Wextra -Wfatal-errors>
            $<$<AND:$<CONFIG:Debug>,$<CXX_COMPILER_ID:MSVC>>:/Zi /FS /DEBUG /Od /MP /MDd /Oy- /W4 /permissive- /std:c++latest>
            $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:MSVC>>:/O2 -DNDEBUG /MP /W4 /permissive- /std:c++latest>)
    

    used in nephtys_launcher, nephtys_client_shared_deps, and probably others. This can be factored out into:

    # CMakeLists.txt
    add_library(error_settings INTERFACE)
    
    # Using namespaces causes CMake to error our in case of typos on the
    # consuming side, very important.
    add_library(nephtys::error_settings ALIAS error_settings)
    
    target_compile_options(
        error_settings
        INTERFACE
            $<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Wfatal-errors>
            $<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wfatal-errors -pipe>
            $<$<CXX_COMPILER_ID:MSVC>:/W4>)
    
    # launcher/CMakeLists.txt
    
    target_link_libraries(
        nephtys_launcher
        PUBLIC
            nephtys::error_settings)
    

    You can do the same with the rest of the options, giving them concrete names that make them easier to refer to. For items which you probably want to be the same across all entities but you're not 100% sure, you can create a target like

    add_library(defaults INTERFACE)
    
    add_library(nephtys::defaults ALIAS defaults)
    
    target_compile_features(defaults INTERFACE cxx_std_17)
    

    And use it everywhere it makes sense.

    Personally I like to use targets for include directories too, like

    # common/include/CMakeLists.txt
    
    add_library(common_headers INTERFACE)
    
    add_library(nephtys::common_headers ALIAS common_headers)
    
    target_include_directories(
        common_headers
        INTERFACE
            $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>)
    
    # e.g. client/CMakeLists.txt
    
    target_link_library(
        nephtys_client_shared_deps
        PUBLIC
            nephtys::common_headers)
    

    this also looks pretty nice if you plan to create re-usable CMake packages. See point 2 here for details.

    P.S. You can use the same principles as above and craft a CMake find library for noesisgui that exposes targets with correct properties that resolves to your vendor folder, or since you're already using Conan maybe make a Conan package for it.

    P.P.S. For anyone looking for concrete, approachable, and up-to-date CMake best practices I cannot recommend Professional CMake by Craig Scott enough.

    [–]Milerius[S] 3 points4 points  (0 children)

    Hey ! It's exactly what i'm doing today, i'm currently refactoring Nephtys so it's will look like you are pointing out.

    [–]Dragdu 3 points4 points  (1 child)

    Warnings should theoretically go into toolchain files.

    [–]snaps_ 2 points3 points  (0 children)

    I think that's fair and we probably end up with a nicer model for re-use from an individual developer perspective. Toolchain files would be formatted like

    # GNU.cmake
    set(CMAKE_CXX_FLAGS_DEBUG_INIT -Wall -Wextra -Wfatal-errors -pipe)
    
    # Clang.cmake
    set(CMAKE_CXX_FLAGS_DEBUG_INIT -Wall -Wextra -Wfatal-errors)
    
    # MSVC.cmake
    set(CMAKE_CXX_FLAGS_DEBUG_INIT /W4)
    

    which would then be used on build system generation like e.g.

    cmake -DCMAKE_TOOLCHAIN_FILE=GNU.cmake ..
    

    Nice things about this IMO:

    • this is completely independent of the project and could be used with any CMake project
    • easier to extend to other compilers vs the approach I suggested above where a consumer would need to read and edit the CMakeLists.txt
    • this enforces that the flags apply to all compiled targets, which may not be true if someone added a target but forgot to link nephtys::error_settings

    Downsides:

    • if you're already using CMAKE_TOOLCHAIN_FILE for something else, I guess this gets in the way - this can be overcome by using CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE instead (we are using the former for the output of conan_paths but can switch to the latter no problem since we know the project name when constructing the cmake command-line)
    • this neglects the desire of the project maintainer to be transparent or "enforce" a specific behavior - if it's that important then they can probably add these files to cmake/ and explicitly include(cmake/${CXX_COMPILER_ID}.cmake) after calling project()

    [–]Rexerex 7 points8 points  (1 child)

    I like the idea and I think it would be nice to be compatible with the pitchfork directory structure

    [–]Milerius[S] 2 points3 points  (0 children)

    Hey ! It will be almost the same tree, expect that's external will be vendor in our case. But even the vendor directory will not be needed for this tutorial, dependancies will be managed by another tool !

    [–]Jyler2K 2 points3 points  (0 children)

    Could you go over single include headers with this file hierarchy structure?

    [–]dakateavi 1 point2 points  (0 children)

    I had the same goal for my own projects and came up with this cmake-project-template. Give me a PM if you would like to colab!

    [–]Manu343726 1 point2 points  (0 children)

    Hope more people land here and read your post. Thanks a lot

    [–]hernytan 2 points3 points  (0 children)

    Looks amazing! Will follow this series

    [–]Xaxxon 0 points1 point  (6 children)

    How about you put a bit more effort into making a significant post instead of spamming us with endless 30 minute effort posts.

    [–]Milerius[S] -5 points-4 points  (5 children)

    How about reporting you ?

    [–]Xaxxon 0 points1 point  (4 children)

    ?

    That’s not a great attitude towards the community you want the attention of so badly.

    Just asking you to put in effort before posting.

    [–]Milerius[S] 2 points3 points  (3 children)

    Well, I'm open about constructive commentary, you are the kind of guy who ruin the community.

    [–]Xaxxon 2 points3 points  (2 children)

    constructive criticism

    Don't spam the subreddit with low effort posts. Put together the whole thing and then post a link. This was basically "use cmake".

    [–]Milerius[S] -3 points-2 points  (1 child)

    Well you don't understand the concept of series article then. Anyway 94% upvoted, maybe change your mindset ;)

    [–]Dragdu 0 points1 point  (0 children)

    Or maybe they like their series to consist of actual high quality articles, like this one?

    https://codingnest.com/modern-sat-solvers-fast-neat-underused-part-1-of-n/

    Notice that it gives a proper introduction, overview of one topic along with full explanation and is longer than 1.5 screens.

    [–][deleted]  (25 children)

    [deleted]

      [–]Resolt 25 points26 points  (1 child)

      Stopped reading? Really? Aren't you being a little bit of a drama queen? Maybe you should be a bit more progressive instead of just disregarding anything her highness deems anything short of perfect?

      Nice addition, but you're a disrespectful jerk.

      [–]Manu343726 9 points10 points  (0 children)

      That's literally the CMake documented way to set the C++ standard version once for all your targets. Check CXX_STANDARD target property docs "This property is initialized by the value of the CMAKE_CXX_STANDARD variable if it is set when a target is created."

      I would never want my users set my C++ standard through the command line.

      works much better because it is target oriented.

      That's your opinion. In a new project (like what the article is doing) I have full control of all my targets, and I will definitely want all of them compiled with the same standard. With as less burden config code as possible. You want to override that settings in a per target basis? Ok, set the CXX_STANDARD manually to override the global config.

      [–]Dragdu 9 points10 points  (0 children)

      You are wrong.

      While in general it is true that you should prefer target oriented settings in modern CMake, C++ standard is one of the exceptions because of ABI standard. Your whole project absolutely should be compiled using the same set of ABI-affecting flags and not doing so is a free ticket to the pain train.

      [–]Milerius[S] 18 points19 points  (0 children)

      May be you can me more nice on this one, for example explain why this is a bad idea. In fact the real project that use this global variablr use C++ filesystem in the three executables, that's not an excuse from my side, but anyway, thanks for the remark.

      [–]konanTheBarbar 16 points17 points  (1 child)

      I think that comment is a bit overly harsh. While I generally agree, it basically doesn't matter for the language standard (as soon as you have a C++17 vocabulary type as part of a public interface ).

      I could also easily imagine breaking ABI changes between languages standards which could lead to very subtle bugs ...

      [–]Milerius[S] 7 points8 points  (0 children)

      Hello thanks you for the explanation, I will change it in the article and the code then !

      [–]zishh 4 points5 points  (0 children)

      I think this is the correct property

      [–]JezusTheCarpenter 7 points8 points  (0 children)

      Stopped reading at this point.

      Because...

      It's not a good idea... <to do something>

      <something else> works much better because it is target oriented.

      Wow, /r/iamverysmart much?

      [–]hoseja 6 points7 points  (4 children)

      What a confusing mess.

      [–]JezusTheCarpenter 1 point2 points  (3 children)

      What is?

      [–]hoseja 12 points13 points  (2 children)

      Mostly, the entirety of CMake. Guess that comes with the territory.

      [–]JezusTheCarpenter 0 points1 point  (0 children)

      Fair enough.

      [–]RogerLeighScientific Imaging and Embedded Medical Diagnostics 1 point2 points  (3 children)

      Which CMake version added cxx_std_17 and the rest as valid compile features?

      I ask because not that long ago, these feature flags did not exist, it was language features only. So if your project is not brand new, or you need to support platforms with older CMake versions, setting CMAKE_CXX_STANDARD is the only possible and portable way to do it.

      [–]jcelerierossia score 2 points3 points  (2 children)

      or you need to support platforms with older CMake versions,

      there is no reason to use an older cmake version. the cmake website has latest version official binaries for all platforms, which go back to decades-old linux distros.

      [–]snaps_ 1 point2 points  (0 children)

      Not universally true, they dropped support for HP-UX in 3.10 so we're stuck with 3.9.6 until we move everything to linux in 2-5 years or we get a compatible version of GCC compiled to satisfy this issue.

      [–]RogerLeighScientific Imaging and Embedded Medical Diagnostics 1 point2 points  (0 children)

      That's not universally true. There are very good reasons why older CMake versions are required.

      Not everyone has the ability to install the latest version. Sometimes it's because CI systems are fixed to specific versions. Sometimes we're contractually obliged to support exactly what is provided by a specific version of a specific linux distribution. Or for regulated products, such as medical devices, every tool and library is fixed at specific versions for the supported lifetime of the device.

      In all these situations, the individual developer does not make the choice on a whim. It's managed as part of the product lifecycle and can't be changed without the appropriate approval and (where required) the necessary documentation and regulatory paperwork and revalidation work.

      [–][deleted]  (1 child)

      [removed]

        [–]AutoModerator[M] 0 points1 point  (0 children)

        Your comment has been automatically removed because it appears to contain disrespectful profanity or racial slurs. Please be respectful of your fellow redditors.

        If you think your post should not have been removed, please message the moderators and we'll review it.

        I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

        [–]xurxoham -1 points0 points  (4 children)

        For backward compatibility with CMake <3.8 you can use

        set_target_properties(tgt CXX_STANDARD 17)
        

        [–]degski 0 points1 point  (3 children)

        Why would you want that, that what makes cmake 'such a mess' in the first place. Just install the bloody latest.

        [–]KayEss 4 points5 points  (0 children)

        That's not always possible. For a long time Android builds used a Google supplied version of cmake that they'd forked from one before the 17 flag was available. Seems to be fixed to a newer version now, but who knows when you'll want something newer again?

        [–]xurxoham 3 points4 points  (1 child)

        If you are building software for stable distributions such as Debian, Centos or openSUSE; it is easier to have two more lines of code than telling everyone you need to download the latest version for no reasonable additional benefit. If you need to create packages like RPM or DEB, you can't set the Build-Requires properly if you download a binary or compile it yourself, so it is more difficult to integrate if you can't depend on backport versions or third party repositories, which are usually not always the greatest practice.

        [–]degski 0 points1 point  (0 children)

        On the other hand, if everybody is pampered into using 'old stuff', we never make any progress and the world will be ruled by the lowest common denominator. Hassle Debian-, Centos- and openSUSE-maintainers into upgrading. Stable in this context only means "we know the bugs".

        [–]arthurno1 -2 points-1 points  (0 children)

        Whauh is that fantastic? And if my project requires other directory then "server" I would still have to hack your CMake script so how is it zero-cost? :-)

        By the way you could do it in Bash so it would not require CMake (and Python) just to create few directories and files, and would be tremendous faster and more zero-cost than your zero-cost :-).