Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for building with CMake #215

Closed
Cazadorro opened this issue Jan 3, 2024 · 38 comments · Fixed by #270
Closed

Add support for building with CMake #215

Cazadorro opened this issue Jan 3, 2024 · 38 comments · Fixed by #270
Labels
⬇️ affects: repo or tools Affects the non-library tools or the repository itself 📁 kind: enhancement New feature or request good first issue Good for newcomers help wanted We need help from the community for this 💪 effort: medium
Milestone

Comments

@Cazadorro
Copy link

Cazadorro commented Jan 3, 2024

I have the fork here: https://github.com/Cazadorro/au/tree/au_cmake Note currently I set cmake standard to C++20, but that is for debugging build issues, I'll set it back to c++14 when it's ready.

Basically right now I'm attempting to get this library to work with cmake. I'm going case by case trying to get this library to work with cmake with different tests.

Some of these tests straight up would never work under MSVC, for example, using M_PI, when #define _USE_MATH_DEFINES is not used. It gave me compile errors, so I had to add code to actually get some tests to compile under MSVC. I also don't see any MSVC configs with Bazel, which is doubly confusing, I was under the impression the library was tested against msvc based on this from the readme

Now I'm getting issues with math_test, here's the first error dump of my compile output:

C:\Repos\au\au/unit_of_measure.hh(809): note: see reference to class template instantiation 'au::InStandardPackOrder<au::Magnitude<>,au::Magnitude<>>' being compiled
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340): error C2794: 'type': is not a member of any direct or indirect base class of 'std::common_type<T,U>'
        with
        [
            T=au::Quantity<au::Meters,int>,
            U=au::Quantity<au::Centi<au::Kelvins>,int>
        ]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au\math_test.cc(325): note: see reference to function template instantiation 'auto au::max<Unit,T>(au::QuantityPoint<Unit,T>,au::QuantityPoint<Unit,T>)' being compiled
        with
        [
            Unit=au::Meters,
            T=int
        ]
C:\Repos\au\au/math.hh(337): note: see reference to function template instantiation 'const _Ty &std::max<au::QuantityPoint<Unit,T>>(const _Ty &,const _Ty &) noexcept(<expr>)' being compiled
        with
        [
            _Ty=au::QuantityPoint<au::Meters,int>,
            Unit=au::Meters,
            T=int
        ]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\utility(75): note: while processing the default template argument of 'au::QuantityPoint<Unit,T>::QuantityPoint(au::QuantityPoint<OtherUnit,OtherRep>)'
        with
        [
            Unit=au::Celsius,
            T=int
        ]
C:\Repos\au\au/quantity_point.hh(104): note: see reference to alias template instantiation 'au::QuantityPoint<Unit,T>::EnableIfImplicitOkIs<true,OtherUnit,OtherRep>' being compiled
        with
        [
            Unit=au::Celsius,
            T=int,
            OtherUnit=au::Meters,
            OtherRep=int
        ]
C:\Repos\au\au/quantity_point.hh(88): note: see reference to function template instantiation 'bool au::QuantityPoint<Unit,T>::should_enable_implicit_construction_from<OtherUnit,OtherRep>(void)' being compiled
        with
        [
            Unit=au::Celsius,
            T=int,
            OtherUnit=au::Meters,
            OtherRep=int
        ]
C:\Repos\au\au/quantity_point.hh(78): note: see reference to function template instantiation 'auto au::operator +<au::Meters,au::Centi<U>,int,int>(au::Quantity<au::Meters,int>,au::Quantity<au::Centi<U>,int>)' being compiled
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/quantity.hh(621): note: see reference to function template instantiation 'auto au::detail::using_common_type<au::Quantity<au::Meters,int>,au::Quantity<au::Centi<U>,int>,au::detail::Plus>(T,au::Quantity<au::Centi<U>,int>,Func)' being compiled
        with
        [
            U=au::Kelvins,
            T=au::Quantity<au::Meters,int>,
            Func=au::detail::Plus
        ]
C:\Repos\au\au/quantity.hh(583): note: see reference to alias template instantiation 'std::common_type_t<T,U>' being compiled
        with
        [
            T=au::Quantity<au::Meters,int>,
            U=au::Quantity<au::Centi<au::Kelvins>,int>
        ]
C:\Repos\au\au/quantity.hh(583): error C2938: 'std::common_type_t' : Failed to specialize alias template
C:\Repos\au\au/quantity.hh(585): error C2057: expected constant expression
C:\Repos\au\au/quantity.hh(447): error C2668: 'au::Quantity<au::Meters,int>::as': ambiguous call to overloaded function
C:\Repos\au\au/quantity.hh(156): note: could be 'auto 
/

Entire version at end.

I'm not sure what the deal is here, these compile errors imply there's some sort of specialization that isn't happening? If I was missing a header, it should have already complained about it in a separate error, but I'm not seeing that happening. I know MSVC has some issues with CRTP and constexpr members, but I don't see that happening here either? And given the above CI tests I would think that this would have been caught if it was a legitimate incompatibility with MSVC?

This test appears to be causing the issue, but there are some weird nonsensical successful compiles and failed compiles in this library with some of the other tests that fail, where I've removed and added tests back in, so I'm not sure how much stock I'd put into this being the issue.

TEST(clamp, QuantityPointTakesOffsetIntoAccount) {
    // Recall that 0 degrees Celsius is 273.15 Kelvins.  We know that `clamp` must take the origin
    // into account for this mixed result.  This means whatever unit we return must be at most 1/20
    // Kelvins, and must evenly divide 1/20 Kelvins.
    constexpr auto celsius_origin = clamp(celsius_pt(0), kelvins_pt(200), kelvins_pt(300));
    ASSERT_TRUE(is_integer(unit_ratio(Kelvins{} / mag<20>(), decltype(celsius_origin)::unit)));
    EXPECT_EQ(celsius_origin, centi(kelvins_pt)(273'15));
}

in particular, if this line remains in the code, and everything below is commented out, the tests fails,

constexpr auto celsius_origin = clamp(celsius_pt(0), kelvins_pt(200), kelvins_pt(300));

However, when everything above is removed, the test compiles fine... and passes... if QuantityPointConsistentWithStdClampWhenTypesAreIdentical and if QuantityPointProducesResultsInCommonUnitOfInputs are the only test remove, the test also passes. If either of those two are not commented out, it doesn't work. If I remove QuantityPointTakesOffsetIntoAccount and comment everything below, things work.

This has already taken more time than I anticipated, I don't think I'll be able to swing trying to also get this tested under clang or gcc right now, I was hoping I could get some help identifying what the issue is.

Entire log for math_test
====================[ Build | math_test | Debug ]===============================
"C:\Program Files\JetBrains\CLion 2022.3\bin\cmake\win\x64\bin\cmake.exe" --build C:\Repos\au\cmake-build-debug --target math_test -j 30
[1/2] Building CXX object au\CMakeFiles\math_test.dir\math_test.cc.obj
FAILED: au/CMakeFiles/math_test.dir/math_test.cc.obj 
C:\PROGRA~2\MICROS~2\2022\BUILDT~1\VC\Tools\MSVC\1438~1.331\bin\Hostx86\x64\cl.exe  /nologo /TP  -IC:\Repos\au -IC:\Repos\au\au -external:IC:\Repos\au\cmake-build-debug\_deps\googletest-src\googletest\include -external:IC:\Repos\au\cmake-build-debug\_deps\googletest-src\googletest -external:IC:\Repos\au\cmake-build-debug\_deps\googletest-src\googlemock\include -external:IC:\Repos\au\cmake-build-debug\_deps\googletest-src\googlemock -external:W0 /DWIN32 /D_WINDOWS /EHsc /Ob0 /Od /RTC1 -std:c++20 -MDd -Zi /permissive /W4 /w14640 /w14242 /w14254 /w14263 /w14265 /w14287 /we4289 /w14296 /w14311 /w14545 /w14546 /w14547 /w14549 /w14555 /w14619 /w14826 /w14905 /w14906 /w14928 /Zc:__cplusplus /DEBUG /showIncludes /Foau\CMakeFiles\math_test.dir\math_test.cc.obj /Fdau\CMakeFiles\math_test.dir\ /FS -c C:\Repos\au\au\math_test.cc
C:\Repos\au\au/utility/string_constant.hh(176): warning C4127: conditional expression is constant
C:\Repos\au\au/utility/string_constant.hh(176): note: consider using 'if constexpr' statement instead
C:\Repos\au\au/utility/string_constant.hh(201): note: while evaluating constexpr function 'au::detail::StringConstant<0>::join'
C:\Repos\au\au/utility/string_constant.hh(196): note: while evaluating constexpr function 'au::detail::join_by'
C:\Repos\au\au/unit_of_measure.hh(83): note: while evaluating constexpr function 'au::detail::concatenate'
C:\Repos\au\au/utility/string_constant.hh(176): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au/units/celsius.hh(35): note: see reference to function template instantiation 'auto au::PrefixApplier<au::Centi>::operator ()<au::Kelvins>(au::QuantityMaker<au::Kelvins>) const' being compiled
C:\Repos\au\au/prefix.hh(35): note: see reference to class template instantiation 'au::QuantityMaker<au::Centi<U>>' being compiled
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/quantity.hh(483): note: see reference to class template instantiation 'au::Centi<U>' being compiled
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/prefix.hh(168): note: see reference to alias template instantiation 'au::detail::ExtendedLabel<1,U>' being compiled
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/unit_of_measure.hh(83): note: see reference to function template instantiation 'au::detail::StringConstant<1> au::detail::concatenate<char[2]>(const char (&)[2])' being compiled
C:\Repos\au\au/utility/string_constant.hh(196): note: see reference to function template instantiation 'au::detail::StringConstant<1> au::detail::join_by<char[1],char[2]>(const SepT (&),const char (&)[2])' being compiled
        with
        [
            SepT=char [1]
        ]
C:\Repos\au\au/utility/string_constant.hh(201): note: see reference to function template instantiation 'au::detail::StringConstant<1> au::detail::StringConstant<0>::join<1>(const au::detail::StringConstant<1> &) const' being compiled
C:\Repos\au\au/utility/string_constant.hh(144): note: see reference to function template instantiation 'void au::detail::StringConstant<0>::join_impl<1,>(char *,const au::detail::StringConstant<1> &) const' being compiled
C:\Repos\au\au/magnitude.hh(412): warning C4127: conditional expression is constant
C:\Repos\au\au/magnitude.hh(412): note: consider using 'if constexpr' statement instead
C:\Repos\au\au/magnitude.hh(412): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au\math_test.cc(94): note: see reference to function template instantiation 'auto au::clamp<au::Kilo<U>,au::Milli<U>,au::Meters,int,int,int>(au::Quantity<au::Kilo<U>,int>,au::Quantity<au::Milli<U>,int>,au::Quantity<au::Meters,int>)' being compiled
        with
        [
            U=au::Meters
        ]
C:\Repos\au\au/math.hh(158): note: while processing the default template argument of 'au::Quantity<au::Milli<U>,int>::Quantity(au::Quantity<OtherUnit,OtherRep>)'
        with
        [
            U=au::Meters
        ]
C:\Repos\au\au/quantity.hh(120): note: see reference to alias template instantiation 'au::Quantity<au::Milli<U>,int>::EnableIfImplicitOkIs<true,OtherUnit,OtherRep>' being compiled
        with
        [
            U=au::Meters,
            OtherUnit=au::Kilo<au::Meters>,
            OtherRep=int
        ]
C:\Repos\au\au/quantity.hh(120): note: see reference to class template instantiation 'au::stdx::conjunction<au::HasSameDimension<U2,T>,au::stdx::disjunction<au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,OtherRep>,au::detail::PermitAsCarveOutForIntegerPromotion<Rep,au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,OtherRep>>>' being compiled
        with
        [
            U2=au::Milli<au::Meters>,
            T=au::Kilo<au::Meters>,
            Rep=int,
            B=au::Prime<2>,
            OtherRep=int
        ]
C:\Repos\au\au/stdx/type_traits.hh(38): note: see reference to class template instantiation 'au::stdx::conjunction<au::stdx::disjunction<au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,OtherRep>,au::detail::PermitAsCarveOutForIntegerPromotion<Rep,au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,OtherRep>>>' being compiled
        with
        [
            Rep=int,
            B=au::Prime<2>,
            OtherRep=int
        ]
C:\Repos\au\au/stdx/type_traits.hh(36): note: see reference to class template instantiation 'au::stdx::disjunction<au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,OtherRep>,au::detail::PermitAsCarveOutForIntegerPromotion<Rep,au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,OtherRep>>' being compiled
        with
        [
            Rep=int,
            B=au::Prime<2>,
            OtherRep=int
        ]
C:\Repos\au\au/stdx/type_traits.hh(46): note: see reference to class template instantiation 'au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,OtherRep>' being compiled
        with
        [
            Rep=int,
            B=au::Prime<2>,
            OtherRep=int
        ]
C:\Repos\au\au/conversion_policy.hh(56): note: see reference to class template instantiation 'au::stdx::disjunction<std::is_floating_point<_Rep>,au::stdx::conjunction<std::is_integral<int>,au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>>' being compiled
        with
        [
            _Rep=int,
            ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,6>,au::Pow<au::Prime<5>,6>>,
            Rep=int
        ]
C:\Repos\au\au/stdx/type_traits.hh(46): note: see reference to class template instantiation 'au::stdx::disjunction<au::stdx::conjunction<std::is_integral<int>,au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>>' being compiled
        with
        [
            ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,6>,au::Pow<au::Prime<5>,6>>,
            Rep=int
        ]
C:\Repos\au\au/stdx/type_traits.hh(44): note: see reference to class template instantiation 'au::stdx::conjunction<std::is_integral<int>,au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>' being compiled
        with
        [
            ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,6>,au::Pow<au::Prime<5>,6>>,
            Rep=int
        ]
C:\Repos\au\au/stdx/type_traits.hh(38): note: see reference to class template instantiation 'au::stdx::conjunction<au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>' being compiled
        with
        [
            ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,6>,au::Pow<au::Prime<5>,6>>,
            Rep=int
        ]
C:\Repos\au\au/stdx/type_traits.hh(38): note: see reference to class template instantiation 'au::stdx::conjunction<au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>' being compiled
        with
        [
            Rep=int,
            ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,6>,au::Pow<au::Prime<5>,6>>
        ]
C:\Repos\au\au/stdx/type_traits.hh(36): note: see reference to class template instantiation 'au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>' being compiled
        with
        [
            Rep=int,
            ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,6>,au::Pow<au::Prime<5>,6>>
        ]
C:\Repos\au\au/conversion_policy.hh(48): note: see reference to function template instantiation 'bool au::can_scale_without_overflow<Rep,au::Pow<B,6>,au::Pow<au::Prime<5>,6>>(au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>,Rep)' being compiled
        with
        [
            Rep=int,
            B=au::Prime<2>
        ]
C:\Repos\au\au/conversion_policy.hh(30): note: see reference to function template instantiation 'T au::get_value<double,au::Pow<B,6>,au::Pow<au::Prime<5>,6>>(au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>)' being compiled
        with
        [
            T=double,
            B=au::Prime<2>
        ]
C:\Repos\au\au/magnitude.hh(523): note: see reference to function template instantiation 'au::detail::MagRepresentationOrError<T> au::detail::get_value_result<T,au::Pow<B,6>,au::Pow<au::Prime<5>,6>>(au::Magnitude<au::Pow<B,6>,au::Pow<au::Prime<5>,6>>)' being compiled
        with
        [
            T=double,
            B=au::Prime<2>
        ]
C:\Repos\au\au/magnitude.hh(494): note: see reference to function template instantiation 'au::detail::MagRepresentationOrError<long double> au::detail::base_power_value<T,6,1,uintmax_t>(B)' being compiled
        with
        [
            T=double,
            B=uintmax_t
        ]
C:\Repos\au\au/packs.hh(348): warning C4296: '>': expression is always false
C:\Repos\au\au/packs.hh(348): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au\math_test.cc(133): note: see reference to function template instantiation 'auto au::clamp<Unit,au::Kelvins,au::Kelvins,T,T,T>(au::QuantityPoint<Unit,T>,au::QuantityPoint<au::Kelvins,T>,au::QuantityPoint<au::Kelvins,T>)' being compiled
        with
        [
            Unit=au::Celsius,
            T=int
        ]
C:\Repos\au\au/math.hh(186): note: see reference to alias template instantiation 'au::CommonPointUnitT<UV,ULo,UHi>' being compiled
        with
        [
            UV=au::Celsius,
            ULo=au::Kelvins,
            UHi=au::Kelvins
        ]
C:\Repos\au\au/math.hh(186): note: see reference to class template instantiation 'au::ComputeCommonPointUnit<UV,ULo,UHi>' being compiled
        with
        [
            UV=au::Celsius,
            ULo=au::Kelvins,
            UHi=au::Kelvins
        ]
C:\Repos\au\au/unit_of_measure.hh(644): note: see reference to alias template instantiation 'au::ComputeCommonPointUnitImpl<UV,ULo,UHi>' being compiled
        with
        [
            UV=au::Celsius,
            ULo=au::Kelvins,
            UHi=au::Kelvins
        ]
C:\Repos\au\au/unit_of_measure.hh(640): note: see reference to alias template instantiation 'au::FlatDedupedTypeListT<au::CommonPointUnit,UV,ULo,UHi>' being compiled
        with
        [
            UV=au::Celsius,
            ULo=au::Kelvins,
            UHi=au::Kelvins
        ]
C:\Repos\au\au/unit_of_measure.hh(640): note: see reference to class template instantiation 'au::FlatDedupedTypeList<au::CommonPointUnit,au::CommonPointUnit<T>,au::CommonPointUnit<au::Kelvins>,au::CommonPointUnit<au::Kelvins>>' being compiled
        with
        [
            T=au::Celsius
        ]
C:\Repos\au\au/packs.hh(418): note: see reference to alias template instantiation 'au::FlatDedupedTypeListT<List,L1,L2>' being compiled
        with
        [
            List=au::CommonPointUnit,
            L1=au::CommonPointUnit<au::Celsius>,
            L2=au::CommonPointUnit<au::Kelvins>
        ]
C:\Repos\au\au/packs.hh(418): note: see reference to class template instantiation 'au::FlatDedupedTypeList<au::CommonPointUnit,au::CommonPointUnit<T>,au::CommonPointUnit<au::Kelvins>>' being compiled
        with
        [
            T=au::Celsius
        ]
C:\Repos\au\au/packs.hh(397): note: see reference to class template instantiation 'au::InOrderFor<List,T,H>' being compiled
        with
        [
            List=au::CommonPointUnit,
            T=au::Celsius,
            H=au::Kelvins
        ]
C:\Repos\au\au/unit_of_measure.hh(637): note: see reference to class template instantiation 'au::InOrderFor<au::UnitProduct,A,B>' being compiled
        with
        [
            A=au::Celsius,
            B=au::Kelvins
        ]
C:\Repos\au\au/unit_of_measure.hh(860): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByUnitAvoidance,au::detail::OrderByDim,au::detail::OrderByMag,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct>' being compiled
        with
        [
            A=au::Celsius,
            B=au::Kelvins
        ]
C:\Repos\au\au/packs.hh(303): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByDim,au::detail::OrderByMag,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct>' being compiled
        with
        [
            A=au::Celsius,
            B=au::Kelvins
        ]
C:\Repos\au\au/packs.hh(303): note: see reference to class template instantiation 'au::LexicographicTotalOrdering<A,B,au::detail::OrderByMag,au::detail::OrderByOrigin,au::detail::OrderAsUnitProduct>' being compiled
        with
        [
            A=au::Celsius,
            B=au::Kelvins
        ]
C:\Repos\au\au/packs.hh(318): note: see reference to class template instantiation 'au::detail::OrderByMag<A,B>' being compiled
        with
        [
            A=au::Celsius,
            B=au::Kelvins
        ]
C:\Repos\au\au/unit_of_measure.hh(809): note: see reference to class template instantiation 'au::InStandardPackOrder<au::Magnitude<>,au::Magnitude<>>' being compiled
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340): error C2794: 'type': is not a member of any direct or indirect base class of 'std::common_type<T,U>'
        with
        [
            T=au::Quantity<au::Meters,int>,
            U=au::Quantity<au::Centi<au::Kelvins>,int>
        ]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au\math_test.cc(325): note: see reference to function template instantiation 'auto au::max<Unit,T>(au::QuantityPoint<Unit,T>,au::QuantityPoint<Unit,T>)' being compiled
        with
        [
            Unit=au::Meters,
            T=int
        ]
C:\Repos\au\au/math.hh(337): note: see reference to function template instantiation 'const _Ty &std::max<au::QuantityPoint<Unit,T>>(const _Ty &,const _Ty &) noexcept(<expr>)' being compiled
        with
        [
            _Ty=au::QuantityPoint<au::Meters,int>,
            Unit=au::Meters,
            T=int
        ]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\utility(75): note: while processing the default template argument of 'au::QuantityPoint<Unit,T>::QuantityPoint(au::QuantityPoint<OtherUnit,OtherRep>)'
        with
        [
            Unit=au::Celsius,
            T=int
        ]
C:\Repos\au\au/quantity_point.hh(104): note: see reference to alias template instantiation 'au::QuantityPoint<Unit,T>::EnableIfImplicitOkIs<true,OtherUnit,OtherRep>' being compiled
        with
        [
            Unit=au::Celsius,
            T=int,
            OtherUnit=au::Meters,
            OtherRep=int
        ]
C:\Repos\au\au/quantity_point.hh(88): note: see reference to function template instantiation 'bool au::QuantityPoint<Unit,T>::should_enable_implicit_construction_from<OtherUnit,OtherRep>(void)' being compiled
        with
        [
            Unit=au::Celsius,
            T=int,
            OtherUnit=au::Meters,
            OtherRep=int
        ]
C:\Repos\au\au/quantity_point.hh(78): note: see reference to function template instantiation 'auto au::operator +<au::Meters,au::Centi<U>,int,int>(au::Quantity<au::Meters,int>,au::Quantity<au::Centi<U>,int>)' being compiled
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/quantity.hh(621): note: see reference to function template instantiation 'auto au::detail::using_common_type<au::Quantity<au::Meters,int>,au::Quantity<au::Centi<U>,int>,au::detail::Plus>(T,au::Quantity<au::Centi<U>,int>,Func)' being compiled
        with
        [
            U=au::Kelvins,
            T=au::Quantity<au::Meters,int>,
            Func=au::detail::Plus
        ]
C:\Repos\au\au/quantity.hh(583): note: see reference to alias template instantiation 'std::common_type_t<T,U>' being compiled
        with
        [
            T=au::Quantity<au::Meters,int>,
            U=au::Quantity<au::Centi<au::Kelvins>,int>
        ]
C:\Repos\au\au/quantity.hh(583): error C2938: 'std::common_type_t' : Failed to specialize alias template
C:\Repos\au\au/quantity.hh(585): error C2057: expected constant expression
C:\Repos\au\au/quantity.hh(447): error C2668: 'au::Quantity<au::Meters,int>::as': ambiguous call to overloaded function
C:\Repos\au\au/quantity.hh(156): note: could be 'auto au::Quantity<au::Meters,int>::as<NewRep,enable_if<au::IsUnit<AssociatedUnit<NewUnit>::type>::value,void>::type>(NewUnit) const'
        with
        [
            NewRep=TargetUnit::Rep,
            NewUnit=TargetUnit::Rep
        ]
C:\Repos\au\au/quantity.hh(146): note: or       'auto au::Quantity<au::Meters,int>::as<NewRep,Unit,void>(NewUnit) const'
        with
        [
            NewRep=TargetUnit::Rep,
            Unit=au::Meters,
            NewUnit=au::Meters
        ]
C:\Repos\au\au/quantity.hh(447): note: while trying to match the argument list '(Unit)'
        with
        [
            Unit=au::Meters
        ]
C:\Repos\au\au/quantity.hh(447): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au/quantity.hh(588): note: see reference to function template instantiation 'auto au::detail::cast_to_common_type<C,au::Meters,int>(au::Quantity<au::Meters,int>)' being compiled
C:\Repos\au\au/quantity.hh(578): note: see reference to function template instantiation 'auto au::rep_cast<TargetUnit::Rep,au::Meters,int>(au::Quantity<au::Meters,int>)' being compiled
C:\Repos\au\au/quantity.hh(578): error C3779: 'au::rep_cast': a function that returns 'auto' cannot be used before it is defined
C:\Repos\au\au/quantity.hh(446): note: see declaration of 'au::rep_cast'
C:\Repos\au\au/quantity.hh(447): error C2668: 'au::Quantity<au::Centi<U>,int>::as': ambiguous call to overloaded function
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/quantity.hh(156): note: could be 'auto au::Quantity<au::Centi<U>,int>::as<NewRep,enable_if<au::IsUnit<AssociatedUnit<NewUnit>::type>::value,void>::type>(NewUnit) const'
        with
        [
            U=au::Kelvins,
            NewRep=TargetUnit::Rep,
            NewUnit=TargetUnit::Rep
        ]
C:\Repos\au\au/quantity.hh(146): note: or       'auto au::Quantity<au::Centi<U>,int>::as<NewRep,Unit,void>(NewUnit) const'
        with
        [
            U=au::Kelvins,
            NewRep=TargetUnit::Rep,
            Unit=au::Centi<au::Kelvins>,
            NewUnit=au::Centi<au::Kelvins>
        ]
C:\Repos\au\au/quantity.hh(447): note: while trying to match the argument list '(Unit)'
        with
        [
            Unit=au::Centi<au::Kelvins>
        ]
C:\Repos\au\au/quantity.hh(447): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au/quantity.hh(588): note: see reference to function template instantiation 'auto au::detail::cast_to_common_type<C,au::Centi<U>,int>(au::Quantity<au::Centi<U>,int>)' being compiled
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/quantity.hh(578): note: see reference to function template instantiation 'auto au::rep_cast<TargetUnit::Rep,au::Centi<U>,int>(au::Quantity<au::Centi<U>,int>)' being compiled
        with
        [
            U=au::Kelvins
        ]
C:\Repos\au\au/quantity.hh(588): error C3889: call to object of class type 'au::detail::Plus': no matching call operator found
C:\Repos\au\au/operators.hh(93): note: could be 'auto au::detail::Plus::operator ()(const T &,const U &) const'
C:\Repos\au\au/quantity.hh(588): note: Failed to specialize function template 'auto au::detail::Plus::operator ()(const T &,const U &) const'
C:\Repos\au\au/quantity.hh(588): note: With the following template arguments:
C:\Repos\au\au/quantity.hh(588): note: 'T=void'
C:\Repos\au\au/quantity.hh(588): note: 'U=void'
C:\Repos\au\au/operators.hh(93): note: you cannot create a reference to 'void'
C:\Repos\au\au/quantity_point.hh(76): error C2062: type 'unknown-type' unexpected
C:\Repos\au\au/quantity_point.hh(79): error C2039: 'value': is not a member of '`global namespace''
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340): error C2794: 'type': is not a member of any direct or indirect base class of 'std::common_type<T,U>'
        with
        [
            T=au::Quantity<au::Meters,int>,
            U=au::Quantity<au::Centi<au::Rankines>,int>
        ]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340): note: the template instantiation context (the oldest one first) is
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\utility(75): note: while processing the default template argument of 'au::QuantityPoint<Unit,T>::QuantityPoint(au::QuantityPoint<OtherUnit,OtherRep>)'
        with
        [
            Unit=au::Fahrenheit,
            T=int
        ]
C:\Repos\au\au/quantity_point.hh(104): note: see reference to alias template instantiation 'au::QuantityPoint<Unit,T>::EnableIfImplicitOkIs<true,OtherUnit,OtherRep>' being compiled
        with
        [
            Unit=au::Fahrenheit,
            T=int,
            OtherUnit=au::Meters,
            OtherRep=int
        ]
C:\Repos\au\au/quantity_point.hh(88): note: see reference to function template instantiation 'bool au::QuantityPoint<Unit,T>::should_enable_implicit_construction_from<OtherUnit,OtherRep>(void)' being compiled
        with
        [
            Unit=au::Fahrenheit,
            T=int,
            OtherUnit=au::Meters,
            OtherRep=int
        ]
C:\Repos\au\au/quantity_point.hh(78): note: see reference to function template instantiation 'auto au::operator +<au::Meters,au::Centi<U>,int,int>(au::Quantity<au::Meters,int>,au::Quantity<au::Centi<U>,int>)' being compiled
        with
        [
            U=au::Rankines
        ]
C:\Repos\au\au/quantity.hh(621): note: see reference to function template instantiation 'auto au::detail::using_common_type<au::Quantity<au::Meters,int>,au::Quantity<au::Centi<U>,int>,au::detail::Plus>(T,au::Quantity<au::Centi<U>,int>,Func)' being compiled
        with
        [
            U=au::Rankines,
            T=au::Quantity<au::Meters,int>,
            Func=au::detail::Plus
        ]
C:\Repos\au\au/quantity.hh(583): note: see reference to alias template instantiation 'std::common_type_t<T,U>' being compiled
        with
        [
            T=au::Quantity<au::Meters,int>,
            U=au::Quantity<au::Centi<au::Rankines>,int>
        ]
C:\Repos\au\au/quantity.hh(447): error C2668: 'au::Quantity<au::Centi<U>,int>::as': ambiguous call to overloaded function
        with
        [
            U=au::Rankines
        ]
C:\Repos\au\au/quantity.hh(156): note: could be 'auto au::Quantity<au::Centi<U>,int>::as<NewRep,enable_if<au::IsUnit<AssociatedUnit<NewUnit>::type>::value,void>::type>(NewUnit) const'
        with
        [
            U=au::Rankines,
            NewRep=TargetUnit::Rep,
            NewUnit=TargetUnit::Rep
        ]
C:\Repos\au\au/quantity.hh(146): note: or       'auto au::Quantity<au::Centi<U>,int>::as<NewRep,Unit,void>(NewUnit) const'
        with
        [
            U=au::Rankines,
            NewRep=TargetUnit::Rep,
            Unit=au::Centi<au::Rankines>,
            NewUnit=au::Centi<au::Rankines>
        ]
C:\Repos\au\au/quantity.hh(447): note: while trying to match the argument list '(Unit)'
        with
        [
            Unit=au::Centi<au::Rankines>
        ]
C:\Repos\au\au/quantity.hh(447): note: the template instantiation context (the oldest one first) is
C:\Repos\au\au/quantity.hh(588): note: see reference to function template instantiation 'auto au::detail::cast_to_common_type<C,au::Centi<U>,int>(au::Quantity<au::Centi<U>,int>)' being compiled
        with
        [
            U=au::Rankines
        ]
C:\Repos\au\au/quantity.hh(578): note: see reference to function template instantiation 'auto au::rep_cast<TargetUnit::Rep,au::Centi<U>,int>(au::Quantity<au::Centi<U>,int>)' being compiled
        with
        [
            U=au::Rankines
        ]
ninja: build stopped: subcommand failed.

<\details>

@Cazadorro Cazadorro changed the title Attempting to make CMake version, many tests pass, but issues on MSVC building some tests, seemingly un-related. Attempting to make CMake version, many tests pass, but issues on MSVC building some tests. Jan 3, 2024
@Cazadorro
Copy link
Author

Cazadorro commented Jan 3, 2024

Tested all tests, the following tests failed to even build math_test, prefix_test, quantity_point_test, quantity_test, and unit_test. All other tests succeed in building and pass. I wonder if some of these issues stem from using types like long and long double, which are different on MSVC vs GCC and Clang, though It's hard to even pinpoint where the error is coming from.

  • math_test fails (shown above)

  • prefix_test,

C:\Repos\au\au/magnitude.hh(529): error C2338: static_assert failed: 'Value outside range of destination type'
C:\Repos\au\au/conversion_policy.hh(48): note: while evaluating constexpr function 'au::can_scale_without_overflow'
C:\Repos\au\au/magnitude.hh(532): error C2338: static_assert failed: 'Unknown error occurred'
C:\Repos\au\au/conversion_policy.hh(48): note: while evaluating constexpr function 'au::can_scale_without_overflow'
C:\Repos\au\au/conversion_policy.hh(48): error C2975: 'B': invalid template argument for 'au::stdx::bool_constant', expected compile-time constant expression
C:\Repos\au\au/stdx/type_traits.hh(29): note: see declaration of 'B'
C:\Repos\au\au/magnitude.hh(532): fatal error C1003: error count exceeds 100; stopping compilation

  • quantity_point_test (with similar error to math_test)
  • quantity_test with similar issue to prefix_test.
  • units_test with:
C:\Repos\au\au/quantity.hh(165): error C2338: static_assert failed: 'Dangerous conversion for integer Rep!  See: https://aurora-opensource.github.io/au/main/troubleshooting/#dangerous-conversion'
C:\Repos\au\au/quantity.hh(165): note: the template instantiation context (the oldest one first) is
C:\Repos\au\cmake-build-debug\_deps\googletest-src\googletest\include\gtest/gtest.h(1398): note: see reference to function template instantiation 'testing::AssertionResult testing::internal::CmpHelperEQ<T1,T2>(const char *,const char *,const T1 &,const T2 &)' being compiled
        with
        [
            T1=au::Quantity<au::Micro<au::Grams>,long>,
            T2=au::Quantity<au::PoundsMass,long>
        ]
C:\Repos\au\cmake-build-debug\_deps\googletest-src\googletest\include\gtest/gtest.h(1379): note: see reference to function template instantiation 'bool au::operator ==<au::Micro<au::Grams>,au::PoundsMass,long,long>(au::Quantity<au::Micro<au::Grams>,long>,au::Quantity<au::PoundsMass,long>)' being compiled
C:\Repos\au\au/quantity.hh(595): note: see reference to function template instantiation 'auto au::detail::using_common_type<au::Quantity<au::Micro<au::Grams>,long>,au::Quantity<au::PoundsMass,long>,au::detail::Equal>(T,U,Func)' being compiled
        with
        [
            T=au::Quantity<au::Micro<au::Grams>,long>,
            U=au::Quantity<au::PoundsMass,long>,
            Func=au::detail::Equal
        ]
C:\Repos\au\au/quantity.hh(588): note: see reference to function template instantiation 'auto au::detail::cast_to_common_type<C,au::PoundsMass,long>(au::Quantity<au::PoundsMass,long>)' being compiled
C:\Repos\au\au/quantity.hh(578): note: see reference to function template instantiation 'auto au::Quantity<au::PoundsMass,long>::as<au::Micro<au::Grams>,void>(NewUnit) const' being compiled
        with
        [
            NewUnit=au::Micro<au::Grams>
        ]
C:\Repos\au\au/magnitude.hh(370): warning C4244: 'initializing': conversion from 'T' to 'long double', possible loss of data
        with
        [
            T=__int64
        ]

@chiphogg chiphogg changed the title Attempting to make CMake version, many tests pass, but issues on MSVC building some tests. Add support for building with CMake Jan 3, 2024
@chiphogg
Copy link
Contributor

chiphogg commented Jan 3, 2024

Wow --- thanks for taking this on! It's very exciting to see this progress. This is a big step towards enabling the community to use Au more fully, and will unblock further improvements later such as Conan availability.

I see that it was confusing that the tests didn't already pass under MSVC. I should be able to clear that up. We added our MSVC support in #151. Since we didn't have build rules that we knew how to get working for MSVC, what we did instead was to make a single-file version of the library, and run basic rudimentary tests, as in the PR. Despite its simplicity, this approach was already enough to expose some issues with Au on MSVC. We figured we could add more tests to this file if we find more problems.

Of course, the ability to run all of the tests (which I think your changes would provide) would be even better!


As for the specific failures themselves, I've given a cursory glance. I think your suggestion that it comes down to different definitions of long (and perhaps long double?) is spot on. I believe the :units_test could be fixed by changing pounds_mass(1L) to pounds_mass(1LL) in au/units/test/grams_test.cc.

This probably also explains the ":prefix_test-style" failures, where we can't fit a value in a destination type, although without the source line number from the test file it's hard to confirm and fix.

The :math_test error is bizarre and concerning. It's a red flag that it's trying to find common units between length (Meters) and temperature (Centi<Kelvins>). It smells like this is related to the weirdness you noticed around the QuantityPointTakesOffsetIntoAccount test case, where commenting out parts of that test and/or content above or below it could affect other tests. Perhaps it's a failure of the preprocessor parsing, and different tests are somehow bleeding into each other? I'm really at a loss here, and that's all I can do for today.

Here's a question: if somebody checks out your branch, how could they run MSVC themselves to iterate on it? I do have access to a Windows computer at home, so maybe I could dig into it at some point.

@chiphogg
Copy link
Contributor

chiphogg commented Jan 3, 2024

By the way, I changed the title of this issue so that it could serve as the "support CMake" issue.

@chiphogg chiphogg added 📁 kind: enhancement New feature or request 💪 effort: medium ⬇️ affects: repo or tools Affects the non-library tools or the repository itself labels Jan 3, 2024
@Cazadorro
Copy link
Author

Here's a question: if somebody checks out your branch, how could they run MSVC themselves to iterate on it? I do have access to a Windows computer at home, so maybe I could dig into it at some point.

I'm using CLion, so somethings are automatically handled for me, for example, CLion ships with CMake and Ninja, so I don't have to point to the location of CMake. As a consequence I tested a configuration with vscode which should be easier to acquire. I tested using vscode on my end, and I couldn't get Ninja to work the same as CLion, so I replaced the default generator on windows to be Visual Studio 17 2022 64bit, that seemed to work in vscode. You don't need CMakePresets to make this work, it just makes it way easier to set up. There's probably a better way to do this with CMakeKits, but I haven't used them yet, Visual studio build tools I think make their own kits available.

I think the following should be all that is required ignoring vscode.

  • You need build tools for visual studio, which will allow you to get all the compiler stuff you need with out needing to actually install visual studio https://visualstudio.microsoft.com/downloads/?q=build+tools I believe build tools actually comes with CMake as well, so you might not need to install CMake, though I think you need to select it during setup for the build tools install.

  • You need CMake, the binary distribution should do, but many tools (including VCPKG!) include their own version https://cmake.org/download/

With out an IDE just from the CLI normally you'd just run (with presets)

cmake --preset [preset chosen] -S [project directory] -B [build artifact output directory]

which would generate the files needed to build for the specified generator, then you would run

cmake --build [build artifact output directory] --target [target to build] --config [debug, release, etc...]

though I think the preset handles the last --config part for you. You should also be able to just run the generator's respective commands, but it's easier to do --build from CMake.

I'll also explain what I did to set up an environment for VScode as well (don't normally use vscode, so I don't know if I did this "right").

It appears as if vscode was able to figure out where the build tools existed, and didn't require me to install CMake, appeared to point to one installed with visual studio build tools automatically

On the VScode side of things, I believe you need to install the following VScode extensions CMake, CMake-tools, and cpptools (which I think goes by C/C++ on vscode now?).

Then you will need to press f1 and find "Cmake: select configure preset" and set one of the non-vcpkg configure presets (there's WindowsDebug, WindowRelease, and WindowsDebWithRelInfo).

Then select the "triangle with a wrench" icon, and click the folded page with "configure". on the right hand side, this will basically do the equivalent of cmake --preset [preset chosen] -S [project directory] -B [build artifact output directory]. Then go to Debug (or Launch, but I think Debug launches the debugger from within Vscode as well?) underneath config, and you should be able to select a target using the pen icon to build and execute.

Again, a lot of this was new to me an hour ago, as I don't use vscode, but hopefully this should allow you to run and execute the code using CMake and also be able to run the debugger.

@Cazadorro
Copy link
Author

Cazadorro commented Jan 4, 2024

@chiphogg I made the changes you suggested for units and now at least those tests work. I investigated more in to prefix_test.cc not working, the same spooky behavior in math_test is seen there. I first eliminated all tests below and including TEST(SiPrefixes, PrefixAppliersPredefined), then the tests ran. If I ran none of the test casses inside the test, but kept the test, it would also work. Then I eliminted everything above, and found that after eliminating everything including TEST(PrefixApplier, ConvertsUnitToCorrespondingPrefixedType) at least the first test case in the group would work EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000)). Then I moved the test from TEST(PrefixApplier, ConvertsUnitToCorrespondingPrefixedType) into TEST(SiPrefixes, PrefixAppliersPredefined) and that didn't work (same errors below), in particular I needed to remove constexpr auto d = inches(2) and EXPECT_THAT(make_milli(inches)(5'777).coerce_in(inches), SameTypeAndValue(5)); (which meant the other line had to be removed too) before compiler errors would go aweay. Then I shuffled things around and it still wouldn't work. At this point I had all the test cases commented out in the test case, so it effectivley looked like this

TEST(SiPrefixes, PrefixAppliersPredefined) {
//    constexpr auto inches = QuantityMaker<Inches>{};
    constexpr auto make_milli = PrefixApplier<Milli>{};

    constexpr auto inches = QuantityMaker<Inches>{};

//    constexpr auto d = inches(2);
//    EXPECT_THAT(d.in(make_milli(inches)), SameTypeAndValue(2'000));
//   EXPECT_THAT(make_milli(inches)(5'777).coerce_in(inches), SameTypeAndValue(5));

    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
//    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
//    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
//    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));
//    EXPECT_EQ(exa(inches)(1), peta(inches)(1000));
// everything else commented below
}

Given this worked with out the test cases from the very first test group, I wanted to see if I could at least get all the test cases to work if the first test cases weren't uncommented. But I ran into more weirdness. The following works

    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
//    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));
   // EXPECT_EQ(exa(inches)(1), peta(inches)(1000));

The following doesn't work (again same error below)

    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));
   // EXPECT_EQ(exa(inches)(1), peta(inches)(1000));

And the following does work

//    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));
   // EXPECT_EQ(exa(inches)(1), peta(inches)(1000));

But the following doesn't work


//    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));
    EXPECT_EQ(exa(inches)(1), peta(inches)(1000));

and in fact, that last line shown above doesn't work unless the first two above test cases are disabled

//    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
//    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));
    EXPECT_EQ(exa(inches)(1), peta(inches)(1000));

re-arranging doesn't appears to fix things, if I move it like so and uncomment the first it doesn't compile (with any combination of the first two):

    EXPECT_EQ(exa(inches)(1), peta(inches)(1000));
    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
//    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
//    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
//    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));

But will still compile in combination with the two it would compile with before.

    EXPECT_EQ(exa(inches)(1), peta(inches)(1000));
//    EXPECT_EQ(quetta(inches)(1), ronna(inches)(1000));
//    EXPECT_EQ(ronna(inches)(1), yotta(inches)(1000));
    EXPECT_EQ(yotta(inches)(1), zetta(inches)(1000));
    EXPECT_EQ(zetta(inches)(1), exa(inches)(1000));

I'm not sure what the deal is here. I don't really see how this is GoogleTests fault, since I eliminated all other test groups from this test. I don't see how it's leaving things behind if that's the problem.


====================[ Build | prefix_test | WindowsDebug ]======================
"C:\Program Files\JetBrains\CLion 2022.3\bin\cmake\win\x64\bin\cmake.exe" --build C:\Repos\au\cmake-build-debug --target prefix_test --config Debug
MSBuild version 17.8.3+195e7f5a3 for .NET Framework

  gmock_main.vcxproj -> C:\Repos\au\cmake-build-debug\lib\Debug\gmock_main.lib
  gtest.vcxproj -> C:\Repos\au\cmake-build-debug\lib\Debug\gtest.lib
  gtest_main.vcxproj -> C:\Repos\au\cmake-build-debug\lib\Debug\gtest_main.lib
  prefix_test.cc
C:\Repos\au\au\utility\string_constant.hh(176,29): warning C4127: conditional expression is constant [C:\Repos\au\cmake-build-debug\au\prefix_test.vcxproj]
  (compiling source file '../../au/prefix_test.cc')
  C:\Repos\au\au\utility\string_constant.hh(201,35):
  while evaluating constexpr function 'au::detail::StringConstant<0>::join'
  C:\Repos\au\au\utility\string_constant.hh(196,12):
  while evaluating constexpr function 'au::detail::join_by'
  C:\Repos\au\au\unit_of_measure.hh(83,49):
  while evaluating constexpr function 'au::detail::concatenate'
  C:\Repos\au\au\utility\string_constant.hh(176,29):
  the template instantiation context (the oldest one first) is
  	C:\Repos\au\au\prefix_test.cc(102,5):
  	see reference to function template instantiation 'auto au::PrefixApplier<au::Ronna>::operator ()<au::Inches>(au::QuantityMaker<au::Inches>) const' being compiled
  	C:\Repos\au\au\prefix.hh(35,40):
  	see reference to class template instantiation 'au::QuantityMaker<au::Ronna<U>>' being compiled
          with
          [
              U=au::Inches
          ]
  	C:\Repos\au\au\quantity.hh(483,38):
  	see reference to class template instantiation 'au::Ronna<U>' being compiled
          with
          [
              U=au::Inches
          ]
  	C:\Repos\au\au\prefix.hh(72,30):
  	see reference to alias template instantiation 'au::detail::ExtendedLabel<1,U>' being compiled
          with
          [
              U=au::Inches
          ]
  	C:\Repos\au\au\unit_of_measure.hh(83,49):
  	see reference to function template instantiation 'au::detail::StringConstant<16> au::detail::concatenate<char[17]>(const char (&)[17])' being compiled
  	C:\Repos\au\au\utility\string_constant.hh(196,12):
  	see reference to function template instantiation 'au::detail::StringConstant<16> au::detail::join_by<char[1],char[17]>(const SepT (&),const char (&)[17])' being compiled
          with
          [
              SepT=char [1]
          ]
  	C:\Repos\au\au\utility\string_constant.hh(201,35):
  	see reference to function template instantiation 'au::detail::StringConstant<16> au::detail::StringConstant<0>::join<16>(const au::detail::StringConstant<16> &) const' being compiled
  	C:\Repos\au\au\utility\string_constant.hh(144,9):
  	see reference to function template instantiation 'void au::detail::StringConstant<0>::join_impl<16,>(char *,const au::detail::StringConstant<16> &) const' being compiled
  
C:\Repos\au\au\magnitude.hh(412,11): warning C4127: conditional expression is constant [C:\Repos\au\cmake-build-debug\au\prefix_test.vcxproj]
  (compiling source file '../../au/prefix_test.cc')
  C:\Repos\au\au\magnitude.hh(412,11):
  the template instantiation context (the oldest one first) is
  	C:\Repos\au\au\prefix_test.cc(102,5):
  	see reference to function template instantiation 'testing::AssertionResult testing::internal::EqHelper::Compare<au::Quantity<au::Ronna<U>,int>,au::Quantity<au::Yotta<U>,int>,0x0>(const char *,const char *,const T1 &,const T2 &)' being compiled
          with
          [
              U=au::Inches,
              T1=au::Quantity<au::Ronna<au::Inches>,int>,
              T2=au::Quantity<au::Yotta<au::Inches>,int>
          ]
  	C:\Repos\au\cmake-build-debug\_deps\googletest-src\googletest\include\gtest\gtest.h(1398,12):
  	see reference to function template instantiation 'testing::AssertionResult testing::internal::CmpHelperEQ<T1,T2>(const char *,const char *,const T1 &,const T2 &)' being compiled
          with
          [
              T1=au::Quantity<au::Ronna<au::Inches>,int>,
              T2=au::Quantity<au::Yotta<au::Inches>,int>
          ]
  	C:\Repos\au\cmake-build-debug\_deps\googletest-src\googletest\include\gtest\gtest.h(1379,7):
  	while processing the default template argument of 'au::Quantity<au::Yotta<U>,int>::Quantity(au::Quantity<OtherUnit,OtherRep>)'
          with
          [
              U=au::Inches
          ]
  	C:\Repos\au\au\quantity.hh(120,33):
  	see reference to alias template instantiation 'au::Quantity<au::Yotta<U>,int>::EnableIfImplicitOkIs<true,OtherUnit,OtherRep>' being compiled
          with
          [
              U=au::Inches,
              OtherUnit=au::Ronna<au::Inches>,
              OtherRep=int
          ]
  	C:\Repos\au\au\quantity.hh(120,1):
  	see reference to class template instantiation 'au::stdx::conjunction<au::HasSameDimension<Unit,OtherUnit>,au::stdx::disjunction<au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,OtherRep>,au::detail::PermitAsCarveOutForIntegerPromotion<Rep,au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,OtherRep>>>' being compiled
          with
          [
              Unit=au::Yotta<au::Inches>,
              OtherUnit=au::Ronna<au::Inches>,
              Rep=int,
              B=au::Prime<2>,
              OtherRep=int
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(38,38):
  	see reference to class template instantiation 'au::stdx::conjunction<au::stdx::disjunction<au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,OtherRep>,au::detail::PermitAsCarveOutForIntegerPromotion<Rep,au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,OtherRep>>>' being compiled
          with
          [
              Rep=int,
              B=au::Prime<2>,
              OtherRep=int
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(36,26):
  	see reference to class template instantiation 'au::stdx::disjunction<au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,OtherRep>,au::detail::PermitAsCarveOutForIntegerPromotion<Rep,au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,OtherRep>>' being compiled
          with
          [
              Rep=int,
              B=au::Prime<2>,
              OtherRep=int
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(46,93):
  	see reference to class template instantiation 'au::detail::CoreImplicitConversionPolicyImpl<Rep,au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,OtherRep>' being compiled
          with
          [
              Rep=int,
              B=au::Prime<2>,
              OtherRep=int
          ]
  	C:\Repos\au\au\conversion_policy.hh(56,13):
  	see reference to class template instantiation 'au::stdx::disjunction<std::is_floating_point<_Rep>,au::stdx::conjunction<std::is_integral<SourceRep>,au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>>' being compiled
          with
          [
              _Rep=int,
              SourceRep=int,
              ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,3>,au::Pow<au::Prime<5>,3>>,
              Rep=int
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(46,38):
  	see reference to class template instantiation 'au::stdx::disjunction<au::stdx::conjunction<std::is_integral<SourceRep>,au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>>' being compiled
          with
          [
              SourceRep=int,
              ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,3>,au::Pow<au::Prime<5>,3>>,
              Rep=int
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(44,26):
  	see reference to class template instantiation 'au::stdx::conjunction<std::is_integral<SourceRep>,au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>' being compiled
          with
          [
              SourceRep=int,
              ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,3>,au::Pow<au::Prime<5>,3>>,
              Rep=int
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(38,38):
  	see reference to class template instantiation 'au::stdx::conjunction<au::IsInteger<ScaleFactor>,au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>' being compiled
          with
          [
              ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,3>,au::Pow<au::Prime<5>,3>>,
              Rep=int
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(38,38):
  	see reference to class template instantiation 'au::stdx::conjunction<au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>>' being compiled
          with
          [
              Rep=int,
              ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,3>,au::Pow<au::Prime<5>,3>>
          ]
  	C:\Repos\au\au\stdx\type_traits.hh(36,26):
  	see reference to class template instantiation 'au::detail::CanScaleThresholdWithoutOverflow<Rep,ScaleFactor>' being compiled
          with
          [
              Rep=int,
              ScaleFactor=au::Magnitude<au::Pow<au::Prime<2>,3>,au::Pow<au::Prime<5>,3>>
          ]
  	C:\Repos\au\au\conversion_policy.hh(48,31):
  	see reference to function template instantiation 'bool au::can_scale_without_overflow<Rep,au::Pow<B,3>,au::Pow<au::Prime<5>,3>>(au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>,Rep)' being compiled
          with
          [
              Rep=int,
              B=au::Prime<2>
          ]
  	C:\Repos\au\au\conversion_policy.hh(30,9):
  	see reference to function template instantiation 'T au::get_value<double,au::Pow<B,3>,au::Pow<au::Prime<5>,3>>(au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>)' being compiled
          with
          [
              T=double,
              B=au::Prime<2>
          ]
  	C:\Repos\au\au\magnitude.hh(523,29):
  	see reference to function template instantiation 'au::detail::MagRepresentationOrError<T> au::detail::get_value_result<T,au::Pow<B,3>,au::Pow<au::Prime<5>,3>>(au::Magnitude<au::Pow<B,3>,au::Pow<au::Prime<5>,3>>)' being compiled
          with
          [
              T=double,
              B=au::Prime<2>
          ]
  	C:\Repos\au\au\magnitude.hh(494,18):
  	see reference to function template instantiation 'au::detail::MagRepresentationOrError<long double> au::detail::base_power_value<T,3,1,uintmax_t>(B)' being compiled
          with
          [
              T=double,
              B=uintmax_t
          ]
 
C:\Repos\au\au\magnitude.hh(529,34): error C2338: static_assert failed: 'Value outside range of destination type' [C:\Repos\au\cmake-build-debug\au\prefix_test.vcxproj]
  (compiling source file '../../au/prefix_test.cc')
  C:\Repos\au\au\conversion_policy.hh(48,62):
  while evaluating constexpr function 'au::can_scale_without_overflow'
  
C:\Repos\au\au\magnitude.hh(532,34): error C2338: static_assert failed: 'Unknown error occurred' [C:\Repos\au\cmake-build-debug\au\prefix_test.vcxproj]
  (compiling source file '../../au/prefix_test.cc')
  C:\Repos\au\au\conversion_policy.hh(48,62):
  while evaluating constexpr function 'au::can_scale_without_overflow'
  
C:\Repos\au\au\conversion_policy.hh(48,62): error C2975: 'B': invalid template argument for 'au::stdx::bool_constant', expected compile-time constant expression [C:\Repos\au\cmake-build-debug\au\prefix_test.vcxproj]
  (compiling source file '../../au/prefix_test.cc')
  C:\Repos\au\au\stdx\type_traits.hh(29,16):
  see declaration of 'B'


@chiphogg
Copy link
Contributor

chiphogg commented Jan 5, 2024

Hmm... the spooky action-at-a-distance feels like something we'll need to root cause to get unblocked.

I think it's worth putting up a checklist for what it would take to get the CMake support landed.

  • Get all tests passing under CMake
  • Create a CI job to run all tests under CMake
  • Update installation instructions documentation (I'll do this once we get here)

Just to set expectations, I find I've been swamped at work since coming back from break (unsurprisingly, given Aurora's 2024 goals). So this will be more something for my "personal project" time for the next little while. And on that front, it's competing with writing papers for the C++ Standard Units Library, which is a higher priority for me but is starting to fall behind. So, this is both explanation and apology as to why I don't expect I'll be able to take a crack at setting up the CMake dev environment any time real soon, despite the fact that this is a really exciting and encouraging development --- and that I'm really grateful for what you're doing here.

However, there's still lots of value in making these posts! If there are other CMake users who want to help out, this issue's contents give them a huge head start. I hope my checklist will also add clarity about the bigger picture of what it takes to get this landable, and where we're at now.

@chiphogg chiphogg added good first issue Good for newcomers help wanted We need help from the community for this labels Jan 5, 2024
@Cazadorro
Copy link
Author

Cazadorro commented Jan 6, 2024

@chiphogg Alright, so I had a thought, does the issue still happen if we use EXPECT_TRUE instead of EXPECT_EQ? So I replaced all the EXPECT_EQ's in TEST(SiPrefixes, PrefixAppliersPredefined), so they instead looked like

   EXPECT_TRUE(quetta(inches)(1) == ronna(inches)(1000));
   EXPECT_TRUE(ronna(inches)(1) == yotta(inches)(1000));
   EXPECT_TRUE(yotta(inches)(1) == zetta(inches)(1000));
   EXPECT_TRUE(zetta(inches)(1) == exa(inches)(1000));
   EXPECT_TRUE(exa(inches)(1) == peta(inches)(1000));

and... it compiled. So I undid commenting out everything else, and the only other part that failed to compile was TEST(BinaryPrefixes, PrefixAppliersPredefined) which also has a bunch of prefix tests. The funny thing is EXPECT_EQ is used in many other places, including the tests above it, TEST(BinaryPrefixes, HaveCorrectAbsoluteValues), where it's used like

EXPECT_EQ(unit_ratio(Yobi<Bytes>{}, Bytes{}), pow<8>(mag<1024>()));

And it doesn't fail to compile. So... I'm not sure what is going on, but this should work on MSVC and GCC/CLang and still result in the same tests. This made me think it actually was a google test related problem, but even with out their complicated macros... weird things happen. I think it may have something to do with how deep the templates goes with matchers causing some compiler error internally to MSVC with some sort of overflow?

So I got prefix_test.cc working, and everything passed. Then I tried to see if the same issue was with quantity_test.cc... and sure enough a single line (where EXPECT_TRUE is below) had to be changed.

TEST(Quantity, HandlesBaseDimensionsWithFractionalExponents) {
    using KiloRootFeet = decltype(root<2>(Mega<Feet>{}));
    constexpr auto x = make_quantity<KiloRootFeet>(5);
    EXPECT_EQ(x.in(root<2>(Feet{})), 5'000);
    EXPECT_TRUE(x * x == mega(feet)(25)); //only changed line in the entire file...
}

But it's strange since a similar line in the subsequent function wasn't needed (though it was comparing doubles there)).

TEST(Quantity, HandlesMagnitudesWithFractionalExponents) {
    constexpr auto x = sqrt(kilo(feet))(3.0);

    // We can retrieve the value in the same unit (regardless of the scale's fractional powers).
    EXPECT_EQ(x.in(sqrt(kilo(feet))), 3.0);

    // We can retrieve the value in a *different* unit, which *also* has fractional powers, as long
    // as their *ratio* has no fractional powers.
    EXPECT_EQ(x.in(sqrt(milli(feet))), 3'000.0);

    // We can also retrieve the value in a different unit whose ratio *does* have fractional powers.
    EXPECT_NEAR(x.in(sqrt(feet)), 94.86833, 1e-5);

    // Squaring the fractional base power gives us an exact non-fractional dimension and scale.
    EXPECT_EQ(x * x, kilo(feet)(9.0));
}

So quanity_test.cc built.

Then I went to quantity_point_test, and sure enough another EXPECT_EQ was causing compile failure (and again, one very similar to it up above didn't have the same issue...)

TEST(QuantityPoint, SupportsDirectAccessWithQuantityMakerOfSameUnit) {
    auto p = meters_pt(3);
    ++(p.data_in(meters_pt));
    EXPECT_EQ(p, meters_pt(4));
}

changed to

TEST(QuantityPoint, SupportsDirectAccessWithQuantityMakerOfSameUnit) {
    auto p = meters_pt(3);
    ++(p.data_in(meters_pt));
    EXPECT_TRUE(p == meters_pt(4));
}

But I ran into an issue with google_test's "matchers" and EXPECT_THAT. and while the "out of order" thing didn't really seem to be happening, at least at the begining, I was seeing strange behavior still, and this time de-sugaring didn't seem to help

I got the following error:

C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794: 'type': is not a member of any direct or indirect base class of 'std::common_type<T,U>' [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:         with [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:         [ [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:             T=au::Quantity<au::Meters,int>, [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:             U=au::Quantity<au::ScaledUnit<au::Kelvins,au::Magnitude<au::Pow<au::Prime<2>,-2>,au::Pow<au::Prime<5>,-1>>>,int> [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:         ] [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]

When a test did the following:

EXPECT_THAT(centi(meters_pt)(75).coerce_as(meters_pt), SameTypeAndValue(meters_pt(0)));

Which made no sense, Where is the kelvins here? I kept moving things around, until I ended up with this mess that finally passed, but I had to completely avoid matchers to do it.

TEST(QuantityPoint, CanImplicitlyConstructWithNonintegerOffsetIffDestinationIsFloatingPoint) {
    {
        using T1 = QuantityPoint<Celsius, int32_t>;
        using T2 = QuantityPoint<Kelvins, int32_t>;
         auto x = std::is_convertible<T1, T2>::value; // this line normally causes issues with the matchers
//        auto x = (std::is_convertible<QuantityPointI32<Celsius>, QuantityPointI32<Kelvins>>::value);
//        EXPECT_FALSE(x);

        auto v = centi(meters_pt)(75).coerce_as(meters_pt);
        using v_type =decltype(v) ;
//        const ::testing::Matcher<const v_type&> matcher = ::testing::SafeMatcherCast<const v_type&>(SameTypeAndValue(meters_pt(0)));

        // The expected path here is that the matcher should match (i.e. that most
        // tests pass) so optimize for this case.
//        auto q = matcher.Matches(v);
//        auto matcher = ::testing::internal::MakePredicateFormatterFromMatcher(SameTypeAndValue(meters_pt(0)));
//        auto q = matcher("", centi(meters_pt)(75).coerce_as(meters_pt));
//        using g_type =decltype( meters_pt(0));
        EXPECT_TRUE((std::is_same_v<v_type, decltype( meters_pt(0))>) && v == meters_pt(0));
    }

//    EXPECT_TRUE((std::is_convertible<QuantityPointI32<Celsius>, QuantityPointD<Kelvins>>::value));
//
//    // Also works for ints if the destination unit evenly divides both offset and initial diff.
//    EXPECT_TRUE(
//        (std::is_convertible<QuantityPointI32<Celsius>, QuantityPointI32<Milli<Kelvins>>>::value));
}

The same error will appear even if I try to get around the GTest macro and manually use the underlying matcher stuff. Some how the mere existence of auto x = std::is_convertible<T1, T2>::value; with Kelvins is causing issues, I don't see how, but it's as if Celcius is being replaced by Meters later in the code at the ast or compiler level (or vice versa). My best guess is there is some sort of bug in MSVC due to how deep google test goes with template instantiation, specifically because of how matchers work. If I make the types both kelvin it works.

I think the quick solution is possibly manually changing stuff that fails to compile with matchers to use EXPECT_TRUE, but only for MSVC when it is detected (since the test failure errors are just worse with out the matchers used here).
But this seems like a MSVC bug. Unfortunately, it's not exactly straight forward to report MSVC bugs, you need a non-anonymous account I think, (and at least here, it seems I can't create any more for this device anymore). Basically I don't have a way to create an account I'm comfortable with associating with my account here, as they'll need to both look at this conversation and earlier commits to my repo.

If you can, can you report this to https://developercommunity.visualstudio.com/cpp and say something like:

"Extensive template usage appears to cause inconsistent compiler behavior, resulting in completely wrong templates being substituted in some instances"

[severity:It’s more difficult to complete my work]
See https://github.com/Cazadorro/au/tree/au_cmake_msvc_bug_reproduction and the specific file https://github.com/Cazadorro/au/blob/au_cmake_msvc_bug_reproduction/au/quantity_point_test.cc and the discussion on github #215. Execute the quantity_point_test target to reproduce.

See lines 72 through 101,

#define MSVC_BUG_TEST_ENABLE_FAILURE_CASE
TEST(QuantityPoint, CanImplicitlyConstructWithNonintegerOffsetIffDestinationIsFloatingPoint) {
    {
        using T1 = QuantityPoint<Celsius, int32_t>;
        using T2 = QuantityPoint<Kelvins, int32_t>;
         auto x = std::is_convertible<T1, T2>::value;
//        auto x = (std::is_convertible<QuantityPointI32<Celsius>, QuantityPointI32<Kelvins>>::value);
//        EXPECT_FALSE(x);

        auto v = centi(meters_pt)(75).coerce_as(meters_pt);
        using v_type =decltype(v) ;

#ifdef MSVC_BUG_TEST_ENABLE_FAILURE_CASE
        const ::testing::Matcher<const v_type&> matcher = ::testing::SafeMatcherCast<const v_type&>(SameTypeAndValue(meters_pt(0)));
#endif
        // The expected path here is that the matcher should match (i.e. that most
        // tests pass) so optimize for this case.
//        auto q = matcher.Matches(v);
//        auto matcher = ::testing::internal::MakePredicateFormatterFromMatcher(SameTypeAndValue(meters_pt(0)));
//        auto q = matcher("", centi(meters_pt)(75).coerce_as(meters_pt));
//        using g_type =decltype( meters_pt(0));
        EXPECT_TRUE((std::is_same_v<v_type, decltype( meters_pt(0))>) && v == meters_pt(0));
    }

//    EXPECT_TRUE((std::is_convertible<QuantityPointI32<Celsius>, QuantityPointD<Kelvins>>::value));
//
//    // Also works for ints if the destination unit evenly divides both offset and initial diff.
//    EXPECT_TRUE(
//        (std::is_convertible<QuantityPointI32<Celsius>, QuantityPointI32<Milli<Kelvins>>>::value));
}

With #define MSVC_BUG_TEST_ENABLE_FAILURE_CASE, the mere creation of const ::testing::Matcher<const v_type&> matcher = ::testing::SafeMatcherCast<const v_type&> seemingly causes the line above it to fail auto x = std::is_convertible<T1, T2>::value;, and likewise, if this line is removed compilation will also succeed, despite the error being:

C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794: 'type': is not a member of any direct or indirect base class of 'std::common_type<T,U>' [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:         with [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:         [ [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:             T=au::Quantity<au::Meters,int>, [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:             U=au::Quantity<au::ScaledUnit<au::Kelvins,au::Magnitude<au::Pow<au::Prime<2>,-2>,au::Pow<au::Prime<5>,-1>>>,int> [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\14.38.33130\include\type_traits(1340,53): error C2794:         ] [C:\Repos\au\cmake-build-debug\au\quantity_point_test.vcxproj]

Due to the inconsistent nature of the bug, this is close to the smallest effective MVCE that can be created.

@chiphogg
Copy link
Contributor

(Sorry I didn't get to this last weekend)

So are you saying that the problems now only happen for CMake and MSVC? I just wanted to get clear on that before reporting a bug to MS.


Anyway, from a big picture point of view, here are my latest thoughts.

I think changing the test contents is a last resort. I don't want us to be constrained from what kind of tests we can write. EXPECT_THAT plus (possibly custom) matchers gives us too much expressiveness for me to give up.

We will always have the bazel builds to make sure the code is thoroughly tested. What's more, testing individual helper libraries on CMake is less important (especially when they're robustly tested by other means). I think what we really need to test here is that each individual "target" (I'm using bazel terminology here; I don't know CMake) can be used correctly in a project.

So maybe the right approach is to make a folder of CMake tests, and just have some barebones tests for each target to prove that the target can work?

@Cazadorro
Copy link
Author

So are you saying that the problems now only happen for CMake and MSVC? I just wanted to get clear on that before reporting a bug to MS.

Yes, though I don't think this is a CMake issue, I'm even using Visual Studio generator (and not ninja) with these issues, so it means that this is identical to if I tried to us msbuild with Visual Studio itself to build this. So there's nothing there that attaches CMake to the utter weirdness of these compile issues, I've only tested this with MSVC, presumably if you didn't have issues before with these tests on GCC or CLang, then neither of those compilers have that problem either, . My understanding from previous correspondence was that all tests passed on GCC and CLang, but only a reduced set of tests were used for MSVC, so what I'm saying now is based off of that information. I don't have a linux system set up at home, but I'll see if I can set up github actions to do this automatically and test with GCC at least, I've not used them before so I'll have to look into how this works, and that will probably take a while.

We will always have the bazel builds to make sure the code is thoroughly tested. What's more, testing individual helper libraries on CMake is less important (especially when they're robustly tested by other means). I think what we really need to test here is that each individual "target" (I'm using bazel terminology here; I don't know CMake) can be used correctly in a project.

So maybe the right approach is to make a folder of CMake tests, and just have some barebones tests for each target to prove that the target can work?

Targets are more or less the same between Bazel and CMake, CMake derived the terms from Make, and I think Bazel did the same. Infact the targets are almost identical between Bazel and CMake here.

That being said, I'm not sure if it makes sense to "test" the targets but I could be wrong. I don't think it's normal to test whether the target itself works or build systems, presumably there isn't a "does bazel work" part of this repo right? By virtue of any of the built test even running, we already know the targets themselves work (au is itself a dependency of the tests, tests all use basically identical target dependencies as Bazel), and would be immediately usable in an add_subdirectory context in CMake.

Installability is different. but AFAIK this library isn't installable right now with bazel any way. And when I talk about installing the library, I mean using the dedicated instrall command necessary command for your build system, and then the user immediately being able to use the library. Looking at the docs, it currently still requires lots of setup in comparison to properly configured CMake projects. Currently, as I've designed the CMake file, the library should already be usable with cmake install. If we get the changes necessary to the main branch, I'll even make it easier, and installable with VCPKG, as in, vcpkg install au or adding it to your vcpkg.json file. Either way this makes using using the library in the end users cmake file as easy as:

find_package(au)

add_exectuable(my_executable) # or any target
target_link_libraries(my_executable PRIVATE au::au)

I can test vcpkg installability locally, as I have a private registry, but the final port file for VCPKG will need to be submitted to VCPKG itself (which I can also do). This will need to refer to a real tagged git commit version, and has to be manually updated updates (automatic pulling of latest commit can work, but causes issues with binary caching with VCPKG, it's expected ports will have pinned versions available). I believe VCPKG itself has some sort of total binary compatibility CI task running, so technically the port should also be tested there, but triaging adding the port itself should get the issue resolved regardless, all of that happens outside of this repo (including the port support, so long as this repo gets cmake support).

@chiphogg
Copy link
Contributor

Cool! Let me try to summarize to make sure we're on the same page generally.

CMake Tests

In order to land CMake support, we'll need to have some tests that build the library using CMake. We'll also need to run those tests automatically.

However, that doesn't mean we have to get all current tests running under CMake. The fact that those tests pass under any build system with a given compiler is enough to give us confidence that the code works for that compiler (I think this is what you were saying in your last comment).

We do still need some tests that make sure that the "main" library targets work in their CMake incarnation (these will be the ones that we'll set up CI runs for). But these tests can be smaller, almost more like smoke tests. (https://github.com/aurora-opensource/au/blob/main/release/au_all_units_hh_test.cc is an example of this.)

So I think the action here is to set up some barebones CMake tests, and create CI jobs to run them automatically.

Installability

Once we get CMake support landed, we'll want to make Au available on conan and vcpkg! In order to do this, it sounds like we'll need to cut a new release. (You mentioned that we could alternatively automatically pull the latest commit, at the cost of having some issues --- but that turns out to not matter, because I strongly prefer to use an immutable tagged and named release anyway.) It seems likely that we'll end up making a 0.3.5 release which is mainly a "CMake support" release, plus perhaps a couple of small improvements that might come along for the ride. After this release gets cut, it sounds like you'll be able to help provide Au on vcpkg. I don't know conan but I'd be willing to try to figure that out.

Installability with bazel

Installability is different. but AFAIK this library isn't installable right now with bazel any way.

Just to be clear: you mean "this library isn't installable right now with bazel for CMake users any way", right? For bazel users the installation process is really smooth AFAIK.

Summary

Based on all of this, I think the updated checklist would look something like:

  • Create barebones/smoke tests for running main targets under CMake
  • Set up CI jobs to run these tests automatically on every PR and commit
  • Update installation instructions

At that point, we could consider this issue resolved.

After that, we'd need to cut a new release, and then make that release available on conan and vcpkg. (And then we'd want to update the installation instructions again.)

Does that all sound about right?

@Cazadorro
Copy link
Author

However, that doesn't mean we have to get all current tests running under CMake. The fact that those tests pass under any build system with a given compiler is enough to give us confidence that the code works for that compiler (I think this is what you were saying in your last comment).

I can't tell if there's confusion here or not, so let me clarify some things.

CMake is a meta build system, technically, it generates build files for a target system (make msbuild, ninja etc...). I don't think I quite understood that Bazel is different here, my understanding now is Bazel is more akin to a cross platform Make? So when I say "CMake can't be causing X issue", it's because CMake isn't what gets run when your project actually builds, it's the generator chosen with CMake (the compiler and real build system combo), it literally cannot be responsible for many of these types of issues, by definition. My understanding is that all these issues with MSVC are just caused by MSVC itself.

On the flip side I consider it a bug if the cmake version of the library isn't able to have complete parity with GCC and Clang in terms of tests. To further clarify there should be no test that Bazel can run period that can't be run under the CMake. If that's the case, it's something I need to fix. To reiterate it was my understanding that the library currently doesn't compile all tests under MSVC with bazel.

But these tests can be smaller, almost more like smoke tests

All tests should run under GCC and CLang, and my understanding is the tests I have gotten to run with CMake for MSVC is a strictly larger set than was able to compile under the previous configuration with Bazel (though that had nothing to do with Bazel, and now should technically be able to run the same tests through MSVC).

it sounds like you'll be able to help provide Au on vcpkg

Yes.

Just to be clear: you mean "this library isn't installable right now with bazel for CMake users any way", right? For bazel users the installation process is really smooth AFAIK.

I misinterpreted the install instructions and didn't see the end for use with bazel.

Based on all of this, I think the updated checklist would look something like:

  • Create barebones/smoke tests for running main targets under CMake

  • Set up CI jobs to run these tests automatically on every PR and commit

  • Update installation instructions

This is about right, though I think all tests should be run with cmake that can, the only things right now that can't be run on MSVC are quantity_tests and prefix_tests (though even then only some tests). We could also conditionally enable tests based on _MSC_VER which will allow tests to run with out changes on all other platforms. At that point, we could actually just straight up have the test run but with worse readability if they fail but only on MSVC by circumventing matchers but only under _MSC_VER .

@chiphogg
Copy link
Contributor

Thanks for helping me fix up my mental model for CMake! You're right: I didn't understand CMake's role as a "meta build system". I have very limited experience with CMake myself. We used it at a former company, but then we switched to bazel after I was there for a year or two, and it was a great relief and I never looked back.

I think this new mental model has some strategic implications as well. Namely: I think it makes sense to focus on getting CI jobs up and running for clang and gcc, because those should be able to run all of the existing tests without modification.

I could try pushing commits to your branch... but I think it might be easier if we turn it into a PR and try to land it in something like in its present state, without any CI tests. We could add comments at the top to say that it's experimental and unsupported, with a TODO(#215), and clean up those comments in later PRs that add the CI jobs. I think it would be easier to iterate if the core CMake code were landed.

So the revised plan might be something like:

  • Make PR out of the "CMake parts" of your branch, and land it without automated tests (we'll consider your manual runs to be enough testing for this step).
  • Have me iterate on a new PR to run all existing tests under clang/CMake and gcc/CMake. (I should be able to find guidance on how to set this up.)
  • Update installation instructions.

Does this plan sound reasonable? If so, go ahead and make the PR for the first step out of the CMake-related changes in your branch!

@chiphogg chiphogg added this to the 0.3.5 milestone Mar 7, 2024
@chiphogg chiphogg pinned this issue Jun 25, 2024
@chiphogg
Copy link
Contributor

Latest updates:

I checked out the branch which @Cazadorro mentioned in the first post here, and started playing with it. I'm really CMake-ignorant, but I was able to figure out how to install a new enough version, and build the code. Nice!

I wasn't able to figure out how to run the tests, though --- that part was extremely confusing. I did find the test binaries inside of the au folder in the build folder, but all I could do was to run the binaries individually. Still, even this was forward progress: when built in the default way, every test I tried passed, including math_test. This validates the hypothesis that the weird build errors were specific to MSVC interacting with GTest.

The comment about CMake being a "meta build system" was very helpful in resetting my mental model, and preparing me to get the most out of tutorials --- thanks for that!


So here's my current thinking as to where to go from here.

  1. I'm going to need to bite the bullet and learn CMake. Even if we landed the code as-is, people are going to have issues, and I'll be on the hook for maintaining it. That's not going to work if I don't understand CMake best practices, and understand our CMake code.

  2. Landing full support in one PR is unwise. It's too much to bite off all at once. It'll be much more tractable if I can understand every piece individually. Then we can keep making forward and incremental progress.

I specifically want to make sure I'm learning modern CMake. I've found various tutorials, and I think Henry Schreiner's is the most promising. Especially based on this line from the front page:

This book tries to solve the problem of the poor examples and best practices that you'll find proliferating the web.

Might as well start out right!

Therefore, here's what I am planning to do.

  1. Do the minimum necessary to get a CMake project with one single library target that has no dependencies, plus the one single target for its unit tests. (stdx is a good candidate.)
  2. Get a GitHub action working that builds the target and runs all tests (i.e., all 1 of them).
  3. Add more targets iteratively, expanding out the dependency tree until we encompass the whole library and all its tests.

I'm feeling more confident that I'll be able to write good CMake, now that I have this plan that breaks it into reasonable chunks. That said, there's no substitute for an expert pair of eyes, to spot things I could easily overlook.

@Cazadorro, I don't think I can formally add you as a reviewer on these PRs, because you're not part of the aurora-opensource org (and I don't think we can change that). However, I'll try to remember to post a comment with each new PR here (and if I forget, they'll be linked automatically anyway). Feel free to leave comments on any PR, before or after landing. Also, feel free to do nothing! You've already been immensely helpful in kick-starting this effort. Having your branch as a "cheat sheet" will absolutely turbo-charge this effort! 🚀

I don't know a good ETA for completion, but I expect to be able to get the first PR or two out within the next two weeks.

@Cazadorro
Copy link
Author

Cazadorro commented Jul 1, 2024

Sorry life got in the way and I completely forgot about this, though I'm glad you're taking the dive into learning CMake. Here's some things you should know:

  • CMake has actual debugging capabilities now, so you can use CLion, Visual studio, and Visual Studio Code to actually step through CMake files execution. This has only really been a thing for the past year+.

  • CMake Presets are also "new-ish", https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html effectively ways to bundle environment variables, build configurations, generators, and cmake file and command line options into a single "configuration" which you can execute with CMake with out manually having to set that all up. You can even do things like disable certain configurations for certain platforms, or make "LinuxRelease" vs "WindowsRelease" configurations, as well as other things.

  • CMake, because it is so easy to install for a platform, is considered portable enough that unless you have a real good reason to (even to the point of ignoring your distros version of CMake) depending on the latest stable version at the time of creation is expected. VCPKG for example installs it's own version of CMake no matter the platform, same thing with CLion, Further more, somethings do not work out unless you use late enough versions, causing hard to find bugs.

  • CMake, in my opinion, is quite easy to use for properly configured libraries as a library consumer. However, it gets much harder for library authors. The one consistent thing I found amount library tutorials is that most do not properly cover creating your own "installable" library. My repo should show what the "modern" way of doing that looks like, but most of that information came from this:

    • https://www.youtube.com/watch?v=m0DwB4OvDXk

    • One major change since then was the introduction of FILE_SET option on target_sources, which allows you to specify a flag that says whether a set of files are header files or module files (currently). Using FILE_SET HEADER allows my headers to properly be propagated to my target include install directory with out manually telling CMake for them to go there, and also allows the IDE an easier time figuring out what files I'm using.

and I think Henry Schreiner's is the most promising. Especially based on this line from the front page:

I've used that site myself, but IMO, the "best" resource on learning CMake is unfortunately https://crascit.com/professional-cmake/ by the same person featured in the earlier CPPCON presentation. I say unfortunate because it's both paid (30 dollars), and they also are a "CMake co-maintainer" thus... have direct oversight and control to fix CMake's notorious documentation and learnability problems... and simply haven't... while selling a book that apparently does so... Despite that it's the most comprehensive resource out there and is kept up to date to my knowledge, even about making things installable.

I wasn't able to figure out how to run the tests, though --- that part was extremely confusing. I did find the test binaries inside of the au folder in the build folder, but all I could do was to run the binaries individually

I screwed up and forgot to put enable_testing() underneath if (${AU_ENABLE_TESTING}) line 35, with that, confusingly a CTest target called "All CTest" appears which can run all the google tests (yes, this is the standard way this works in CMake, CTest is "built in" to CMake). Visual studio, Visual studio code, and Clion all understand CMake targets and will automatically populate a list of them you can run (so finding them won't be an issue).

chiphogg added a commit that referenced this issue Jul 2, 2024
We start by creating the top-level `CMakeLists.txt` file, following the
modern CMake guide (https://cliutils.gitlab.io/modern-cmake/).

We already have a `build/` directory, so we'll use `cmake/build` for our
build folder.  We add this to `.gitignore` (using a `*` so as to support
multiple build folders).

We update the release instructions so as to keep the version number in
sync, and the installation instructions so as to explain how to use
CMake.

Helps #215.
@chiphogg
Copy link
Contributor

chiphogg commented Jul 2, 2024

Life does tend to do that. 🙂

Here's the first PR, single-purpose focused on laying the foundations (#254). @Cazadorro, if this looks good, I can get a blocking reviewer to review it. The next PR would add a target for stdx and its test stdx_test, and would add instructions for running tests.

@Cazadorro
Copy link
Author

@chiphogg Looks good to me!

@chiphogg
Copy link
Contributor

chiphogg commented Jul 2, 2024

Thanks! For future reference, you're more than welcome to comment on the PRs themselves, which would make it easier to give feedback for future, presumably-less-trivial PRs. I just can't actually add you to the list-of-official reviewers, that's all. 🙂

chiphogg added a commit that referenced this issue Jul 2, 2024
We start by creating the top-level `CMakeLists.txt` file, following the
modern CMake guide (https://cliutils.gitlab.io/modern-cmake/).

We already have a `build/` directory, so we'll use `cmake/build` for our
build folder.  We add this to `.gitignore` (using a `*` so as to support
multiple build folders).

We update the release instructions so as to keep the version number in
sync, and the installation instructions so as to explain how to use
CMake.

Helps #215.
@chiphogg
Copy link
Contributor

I'm reading through the Professional CMake book. I think I need to pay attention to the packaging and installation sections.

Currently, I'm thinking it's related to the fact that we never call install(). 😅 Maybe I can figure it out between reading the book and using Cazadorro/au as a cheat sheet yet again.

chiphogg added a commit that referenced this issue Jul 12, 2024
@Cazadorro
Copy link
Author

Cazadorro commented Jul 12, 2024

@chiphogg There are two typical to consume a cmake library. As an installed library (so either you or some package manager calling CMake install) where the files are placed in some platform/package manager defined location, or as a subdirectory, where the library isn't installed at all, but instead uses the top level CMakeLists.txt. To get access to properly configured installed libraries, you use find_package(package_name) To use subdirectory cmake projects, use add_subdirectory("path/to/cmakelist.txt") and the targets exposed there will be available to the top level cmake and exposed to subsequent cmake files called in add_subdirectory. Add subdirectory is kind of like "including" the cmake file targets instead of installing it.

You typically avoid add_subdirectory if you've got access to a package manager, but it's bad practice to have it a project that is usable as a subdirectory but not as an installed library, or vice versa. It's pretty obvious why you'd want to have a library be installable, but if you don't have it usable in add_subdirectory, some systems that don't support package managers or in some cases offline systems, will have a hard time using that package. If you're not doing anything super weird (this project doesn't) and you follow how the professional cmake book demonstrates how to make an installable library, your project should be this way by default. The two big things are to make sure aliased targets (ie Au::target) are also exposed in the top level cmake and not just created at install, and that find_package dependencies also obey this rule, or you check manually for those targets existence before calling find_pacakge if they don't (find_package will work on targets which have been properly configured and acquired through add_subdirectory instead of actually being installed on the system). The latter is typically a problem with projects that massively change naming convention at INSTALL_INTERFACE vs BUILD_INTERFACE, which happens way more often than you'd think (one is capital, one is lower case, or adds numbers etc...)

@chiphogg
Copy link
Contributor

I have to pack up for a trip, and I just saw that you left this message, after I spent the last couple hours trying to get it to work!

I'll read it later today or tomorrow, but meanwhile, #265 (draft!) is my progress report. The PR summary contains repro instructions. On the off chance that I'm really close, let me know if I've missed something obvious. Otherwise I'll read the message when I get a chance and try further.

@chiphogg
Copy link
Contributor

chiphogg commented Jul 12, 2024 via email

chiphogg added a commit that referenced this issue Jul 16, 2024
We may have gotten the project to _build_ under CMake, but _other_
projects can't actually _use_ it unless we provide `install` commands.

First, we add an `install(TARGETS...)` command (based on the
`GNUInstallDirs` module) to the `header_only_library()` function.  We
install it as part of an export set, whose name we set with a global
variable (which must be defined before we load this file).

The tricky bit is that this can't work unless the actual code itself is
in a subdirectory of the repo, _not_ the root folder, for reasons I only
dimly understand.  To make this work, we now provide the Au code via a
symlink, which we create inside of a new `cmake/project_symlinks`
folder.  This lets us refer to this folder in the `BASE_DIRS` variable.

In the main CMake file, we add the installation command for the export
set we've been building up.  We also write and install the package
config and version files.

The above changes would break bazel support, because there would be
BUILD files reachable underneath `cmake`, both via the `au` symlink, and
via `cmake/build`.  To fix this, we make `cmake/` a fake local
repository, causing bazel to skip it.

To test, create a new project with two files:

`main.cc`:

```cpp
#include "au/au.hh"

int main(int argc, char **argv) { return 0; }
```

`CMakeLists.txt`:

```cmake
cmake_minimum_required(VERSION 3.29)

project(CppUnitsCompare)

include(FetchContent)
FetchContent_Declare(
  Au
  GIT_REPOSITORY https://github.com/aurora-opensource/au
  GIT_TAG "chiphogg/cmake-install#215"
)
FetchContent_MakeAvailable(Au)

add_executable(CppUnitsCompare main.cpp)
target_link_libraries(CppUnitsCompare PUBLIC Au::au)
```

And execute:

```sh
cmake -S . -B build
cmake --build build
```

Helps #215.
chiphogg added a commit that referenced this issue Jul 18, 2024
For now, we simply run "the default CMake generator on the latest
Ubuntu".  Later on, if we tighten up our notion of "supported CMake
configurations", we can add more options here.

Helps #215.

---------

Co-authored-by: Tim Hirsh <[email protected]>
chiphogg added a commit that referenced this issue Jul 20, 2024
This replaces the `project_symlinks` approach we had been using for
CMake: it turns out that symlinks are fraught and error prone on
Windows.

To make this work, we had to satisfy a number of challenging
constraints:

1. The include path had to be `"au/..."` (e.g., `"au/io.hh"`) for all
   files, on both bazel and CMake.

2. The `au` folder that contained the actual code needed to live inside
   of a true subdirectory --- not the git repository root folder (as it
   has been since time immemorial), and not a symlink (as we had been
   using to get CMake support).

3. The git repository root folder needed to also be the root of the
   bazel folder, because we use that for all of our bazel-backed tools,
   and because we want users to be able to run all `bazel` commands from
   the repo root.

I previously tried a variety of approaches here, including:

- Having CMake create the symlink, instead of having it checked in (bad
  idea: you can't create symlinks in Windows without developer mode, and
  even after I enabled dev mode it didn't work).

- Making a `code` subfolder in the git repository root that had _its own
  separate `WORKSPACE` file_, and was included as a local bazel repo
  (terrible idea: we'd need two copies of all auxiliary bazel files, and
  making the single-file script work was nightmarishly complex --- I
  never even fully got there).

In the end, the approach that works is to use the `includes` attribute
of the `cc_library` rule.  This is simple and direct: it's designed to
solve this exact problem.  The only real downside is that we need to
prepend `code/au` to a lot of files in the `BUILD.bazel` for `//au`, and
also in a couple of places in the single file script.

Helps #215.
@chiphogg
Copy link
Contributor

The finish line is in sight!

After that, it'll be time to cut the 0.3.5 release. 😎

chiphogg added a commit that referenced this issue Jul 22, 2024
This replaces the `project_symlinks` approach we had been using for
CMake: it turns out that symlinks are fraught and error prone on
Windows.  After this PR, CMake support on Windows is confirmed to work!

To make this work, we had to satisfy a number of challenging
constraints:

1. The include path had to be `"au/..."` (e.g., `"au/io.hh"`) for all
   files, on both bazel and CMake.

2. The `au` folder that contained the actual code needed to live inside
   of a true subdirectory --- not the git repository root folder (as it
   has been since time immemorial), and not a symlink (as we had been
   using to get CMake support).

3. The git repository root folder needed to also be the root of the
   bazel folder, because we use that for all of our bazel-backed tools,
   and because we want users to be able to run all `bazel` commands from
   the repo root.

I previously tried a variety of approaches here, including:

- Having CMake create the symlink, instead of having it checked in (bad
  idea: you can't create symlinks in Windows without developer mode).

- Making a `code` subfolder in the git repository root that had _its own
  separate `WORKSPACE` file_, and was included as a local bazel repo
  (terrible idea: we'd need two copies of all auxiliary bazel files, and
  making the single-file script work was nightmarishly complex --- I
  never even fully got there).

In the end, the approach that works is to use the `includes` attribute
of the `cc_library` rule.  This is simple and direct: it's designed to
solve this exact problem.  The only real downside is that we need to
prepend `code/au` to a lot of files in the `BUILD.bazel` for `//au`, and
also in a couple of places in the single file script.

Helps #215.
chiphogg added a commit that referenced this issue Jul 22, 2024
This lets us support installing the library to the system, and then
using it via `find_package()`.

To test this, I ran `sudo cmake --install cmake/build`, observed that it
completed with no errors, and then made a new CMake package that simply
used `find_package(Au)`.  Everything worked!

Helps #215.
chiphogg added a commit that referenced this issue Jul 22, 2024
This lets us support installing the library to the system, and then
using it via `find_package()`.

To test this, I ran `sudo cmake --install cmake/build`, observed that it
completed with no errors, and then made a new CMake package that simply
used `find_package(Au)`.  Everything worked!

Helps #215.
chiphogg added a commit that referenced this issue Jul 22, 2024
To test, I ran `au-docs-serve`, and did an A/B comparison with respect
to the currently deployed doc website.  Looks like the table got updated
correctly, the tabbed instructions work, and everything else was as
expected.

Fixes #215.
@chiphogg chiphogg unpinned this issue Jul 23, 2024
@Cazadorro
Copy link
Author

Cazadorro commented Sep 5, 2024

@chiphogg Hopefully this is fine to ping in this thread, but I wasn't sure where this should go.

This validates the hypothesis that the weird build errors were specific to MSVC interacting with GTest.

I just found this thread today: mpusz/mp-units#518 and it's possible they are experiencing similar issues (even though it's seemingly c++20 related) with MSVC, weird issues that are hard to nail down with out the example being "complicated" enough. If Microsoft fixes this it may help the issues here.

However In the cpp reddit, one of the MSVC standard library maintainers said that compiler work is currently not a priority at Microsoft "but that should change soon", if you look there's very little support for C++23 language features and basically zero work on C++26 (which I don't think is done yet, but both GCC and Clang managed to complete most of the current features). I'm not sure when this will change or if we're entering a new era of MSVC being way behind.

@chiphogg
Copy link
Contributor

chiphogg commented Sep 8, 2024

Yep, this is definitely a good place to ping.

I'm a watcher on mp-units, so I did see that thread. It hadn't occurred to me to connect it to our issues. In any case, it'd be lovely if MSVC could find and fix these issues. 🙂

@ericriff
Copy link
Contributor

I just came across this thread and I want to ask for feedback for #297
Why do the installed targets depend on GTest? #268

@chiphogg
Copy link
Contributor

Why do the installed targets depend on GTest? #268

We have one target, testing, which contains some utilities (e.g., custom googletest matchers) that depend on GTest. So that target, at least, would need the GTest dependency in order to work. Of course, not all users would necessarily want to use that target.

Also, for broader context, the team is definitely bazel-first in terms of our day-to-day experience. We're excited to support CMake and conan, but since we don't use them on a regular basis, we're likely to get things wrong or make surprising choices out of ignorance and inexperience. 🙂 So please take that into account, and feel free to offer guidance on best practices in these domains!

@ericriff
Copy link
Contributor

ericriff commented Sep 30, 2024

So from the CMake point of view, is there a reason why you want to split au into multiple libraries?
I think it would be way easier to just define a single au library and then let the consumer use what they need by just including the required header.
E.g. if you want io support, just include io.hh.

This being just headers I don't understand why you need to explicitly declare that the chrono_interop header depends on these 3 libs prefix, quantity, units

@ericriff
Copy link
Contributor

I've created this rough PR to illustrate what I meant #298

@chiphogg
Copy link
Contributor

chiphogg commented Oct 1, 2024

So from the CMake point of view, is there a reason why you want to split au into multiple libraries? I think it would be way easier to just define a single au library and then let the consumer use what they need by just including the required header. E.g. if you want io support, just include io.hh.

This being just headers I don't understand why you need to explicitly declare that the chrono_interop header depends on these 3 libs prefix, quantity, units

This is really valuable feedback. Thanks! It shows me some things I hadn't even considered. And I think I can explain why things are the way they are.

The short answer is that our bazel libraries are that way because in bazel, it actually matters --- due to sandboxing, everything's hermetic, so it keeps our dependency tree clean. And our CMake libraries are that way because we simply ported them from bazel! But they don't have to stay that way.

On the bazel size, it's critically important to me that the Au library is composed out of many, well-defined, single-purpose targets (which I assume that was what you meant to say instead of "libraries"? edit: nope, I just realized, these are library targets). I want a crisp and clear dependency graph that I can reason about, and interrogate with tooling. This is both for basic architectural reasons, and directly practical reasons: the finer and better defined my targets, the more I can take advantage of bazel's caching for builds and tests.

For CMake, though, you've helped me realize that it doesn't need to be this way! The fact that this structure exists in bazel is enough to keep the library structure clean. I can see how it would be convenient to have a single target for the library --- or, perhaps, two, with a separate target for the libraries that depend on GTest.

When I read this post, I had been planning to explore this route. I still am, but I see you've posted an (explanation-only) PR, so I'll read that first to learn what I can.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⬇️ affects: repo or tools Affects the non-library tools or the repository itself 📁 kind: enhancement New feature or request good first issue Good for newcomers help wanted We need help from the community for this 💪 effort: medium
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants