mirror of
https://github.com/Kitware/CMake.git
synced 2026-11-06 05:16:58 +00:00
install(SBOM) could crash for dependencies outside an export set and mishandle private dependencies expressed through LINK_ONLY. Resolve link expressions before target lookup, traverse unexported dependencies transitively, and isolate document state per configuration. Preserve target license precedence and align document metadata inheritance with CPS. Add regression coverage for these cases. Fixes: #28141 Fixes: #28210
276 lines
6.6 KiB
C++
276 lines
6.6 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include <cm/type_traits>
|
|
|
|
#include "cmSbomSerializer.h"
|
|
|
|
class cmSbomObject;
|
|
|
|
template <typename T>
|
|
inline void SerializeDispatch(T const& t, cmSbomSerializer& serializer)
|
|
{
|
|
serializer.BeginObject();
|
|
t.Serialize(serializer);
|
|
serializer.EndObject();
|
|
}
|
|
|
|
template <typename T>
|
|
inline void SerializeDispatch(T* p, cmSbomSerializer& serializer)
|
|
{
|
|
if (p->SpdxId) {
|
|
serializer.AddReference(*p->SpdxId);
|
|
}
|
|
}
|
|
|
|
inline void SerializeDispatch(std::nullptr_t, cmSbomSerializer&)
|
|
{
|
|
}
|
|
|
|
struct ObjectInterface
|
|
{
|
|
virtual ObjectInterface* Copy(void* storage) const = 0;
|
|
virtual ObjectInterface* Move(void* storage) = 0;
|
|
virtual void* Addr() noexcept = 0;
|
|
virtual void const* Addr() const noexcept = 0;
|
|
virtual void SerializeImpl(cmSbomSerializer& os) const = 0;
|
|
virtual ~ObjectInterface() = default;
|
|
|
|
ObjectInterface() = default;
|
|
ObjectInterface(ObjectInterface const&) = default;
|
|
ObjectInterface& operator=(ObjectInterface const&) = default;
|
|
ObjectInterface(ObjectInterface&&) noexcept = default;
|
|
ObjectInterface& operator=(ObjectInterface&&) noexcept = default;
|
|
};
|
|
|
|
namespace impl {
|
|
|
|
#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20150623
|
|
using max_align_t = ::max_align_t;
|
|
#else
|
|
using max_align_t = std::max_align_t;
|
|
#endif
|
|
|
|
enum class StorageKind
|
|
{
|
|
Stack,
|
|
Heap
|
|
};
|
|
|
|
template <typename, StorageKind>
|
|
struct Storage
|
|
{
|
|
};
|
|
|
|
template <typename T>
|
|
struct Storage<T, StorageKind::Stack> : ObjectInterface
|
|
{
|
|
using ValueType = cm::decay_t<T>;
|
|
|
|
explicit Storage(T x)
|
|
: Value(std::move(x))
|
|
{
|
|
}
|
|
|
|
Storage(Storage&&) noexcept(
|
|
std::is_nothrow_move_constructible<ValueType>::value) = default;
|
|
~Storage() override = default;
|
|
|
|
void* Addr() noexcept override { return std::addressof(Value); }
|
|
void const* Addr() const noexcept override { return std::addressof(Value); }
|
|
|
|
ValueType& get() { return Value; }
|
|
ValueType const& get() const { return Value; }
|
|
|
|
private:
|
|
ValueType Value;
|
|
};
|
|
|
|
template <typename T>
|
|
struct Storage<T, StorageKind::Heap> : ObjectInterface
|
|
{
|
|
using ValueType = cm::decay_t<T>;
|
|
|
|
explicit Storage(T x)
|
|
: Ptr(new T(std::move(x)))
|
|
{
|
|
}
|
|
|
|
Storage(Storage&&) noexcept = default;
|
|
~Storage() override = default;
|
|
|
|
void* Addr() noexcept override { return Ptr.get(); }
|
|
void const* Addr() const noexcept override { return Ptr.get(); }
|
|
|
|
ValueType& get() { return *Ptr; }
|
|
ValueType const& get() const { return *Ptr; }
|
|
|
|
private:
|
|
std::unique_ptr<ValueType> Ptr;
|
|
};
|
|
|
|
template <>
|
|
struct Storage<std::nullptr_t, StorageKind::Stack> : ObjectInterface
|
|
{
|
|
explicit Storage(std::nullptr_t) {}
|
|
|
|
Storage(Storage&&) noexcept = default;
|
|
~Storage() override = default;
|
|
|
|
void* Addr() noexcept override { return nullptr; }
|
|
void const* Addr() const noexcept override { return nullptr; }
|
|
|
|
void SerializeImpl(cmSbomSerializer&) const override {}
|
|
|
|
std::nullptr_t get() { return nullptr; }
|
|
std::nullptr_t get() const { return nullptr; }
|
|
};
|
|
|
|
}
|
|
|
|
struct ObjectStorage
|
|
{
|
|
template <typename T>
|
|
using Stack = impl::Storage<T, impl::StorageKind::Stack>;
|
|
template <typename T>
|
|
using Heap = impl::Storage<T, impl::StorageKind::Heap>;
|
|
|
|
static constexpr std::size_t BufferSize = 128u;
|
|
|
|
static constexpr std::size_t Size = sizeof(Heap<std::nullptr_t>) > BufferSize
|
|
? sizeof(Heap<std::nullptr_t>)
|
|
: BufferSize;
|
|
static constexpr std::size_t Align = alignof(impl::max_align_t);
|
|
|
|
struct Buffer
|
|
{
|
|
alignas(Align) unsigned char Data[Size];
|
|
};
|
|
|
|
template <typename Concrete>
|
|
using Model = cm::conditional_t<sizeof(Stack<Concrete>) <= Size &&
|
|
alignof(Stack<Concrete>) <= Align,
|
|
Stack<Concrete>, Heap<Concrete>>;
|
|
};
|
|
|
|
class cmSbomObject
|
|
{
|
|
public:
|
|
template <typename T>
|
|
struct Instance : ObjectStorage::Model<T>
|
|
{
|
|
using Base = ObjectStorage::Model<T>;
|
|
using Base::Base;
|
|
Instance(Instance&&) noexcept = default;
|
|
|
|
ObjectInterface* Copy(void* storage) const override
|
|
{
|
|
return ::new (storage) Instance(this->get());
|
|
}
|
|
|
|
ObjectInterface* Move(void* storage) override
|
|
{
|
|
return ::new (storage) Instance(std::move(*this));
|
|
}
|
|
|
|
void SerializeImpl(cmSbomSerializer& os) const override
|
|
{
|
|
SerializeDispatch(this->get(), os);
|
|
}
|
|
};
|
|
|
|
cmSbomObject() { ::new (Storage()) Instance<std::nullptr_t>(nullptr); }
|
|
cmSbomObject(std::nullptr_t)
|
|
: cmSbomObject()
|
|
{
|
|
}
|
|
|
|
template <
|
|
typename T, typename Decayed = cm::remove_cv_t<cm::remove_reference_t<T>>,
|
|
typename = cm::enable_if_t<!std::is_same<Decayed, cmSbomObject>::value>>
|
|
cmSbomObject(T&& x)
|
|
{
|
|
::new (Storage()) Instance<Decayed>(std::forward<T>(x));
|
|
}
|
|
|
|
cmSbomObject(cmSbomObject const& other)
|
|
{
|
|
other.Interface().Copy(Storage());
|
|
}
|
|
cmSbomObject(cmSbomObject&& other) noexcept
|
|
{
|
|
other.Interface().Move(Storage());
|
|
}
|
|
|
|
~cmSbomObject() noexcept { Interface().~ObjectInterface(); }
|
|
|
|
cmSbomObject& operator=(cmSbomObject rhs)
|
|
{
|
|
Interface().~ObjectInterface();
|
|
rhs.Interface().Move(Storage());
|
|
return *this;
|
|
}
|
|
|
|
bool IsNull() const noexcept { return Interface().Addr() == nullptr; }
|
|
explicit operator bool() const noexcept { return !IsNull(); }
|
|
|
|
void Serialize(cmSbomSerializer& os) const { Interface().SerializeImpl(os); }
|
|
|
|
template <typename T>
|
|
T& CastUnchecked()
|
|
{
|
|
return *static_cast<T*>(Interface().Addr());
|
|
}
|
|
|
|
template <typename T>
|
|
T const& CastUnchecked() const
|
|
{
|
|
return *static_cast<T const*>(Interface().Addr());
|
|
}
|
|
|
|
ObjectInterface& Interface()
|
|
{
|
|
return *static_cast<ObjectInterface*>(Storage());
|
|
}
|
|
ObjectInterface const& Interface() const
|
|
{
|
|
return *static_cast<ObjectInterface const*>(Storage());
|
|
}
|
|
|
|
void* Storage() { return &Data.Data; }
|
|
void const* Storage() const { return &Data.Data; }
|
|
|
|
template <typename U>
|
|
U* ptr() noexcept
|
|
{
|
|
return std::addressof(this->template CastUnchecked<U>());
|
|
}
|
|
|
|
template <typename U>
|
|
U const* ptr() const noexcept
|
|
{
|
|
return std::addressof(this->template CastUnchecked<U>());
|
|
}
|
|
|
|
private:
|
|
ObjectStorage::Buffer Data{};
|
|
};
|
|
|
|
template <typename T, typename U = cm::decay_t<T>>
|
|
U* insert_back(std::vector<cmSbomObject>& vec, T&& obj) noexcept
|
|
{
|
|
// Returned pointers must survive reallocation of the containing vector.
|
|
static_assert(
|
|
std::is_same<ObjectStorage::Model<U>, ObjectStorage::Heap<U>>::value,
|
|
"insert_back requires heap storage for stable addresses");
|
|
vec.emplace_back(std::forward<U>(obj));
|
|
return std::addressof(vec.back().CastUnchecked<U>());
|
|
}
|