mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-04 12:49:36 -06:00
Adds definitions for representing software bill of materials (SBOM) information and generating SPDX SBOMs. Issue: #26719 Issue: #27001
271 lines
6.3 KiB
C++
271 lines
6.3 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 <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
|
|
{
|
|
vec.emplace_back(std::forward<U>(obj));
|
|
return std::addressof(vec.back().CastUnchecked<U>());
|
|
}
|