Repository: facebook/folly
Branch: main
Commit: 8539216d20b7
Files: 3157
Total size: 23.2 MB
Directory structure:
gitextract_rc_779ue/
├── .buckconfig
├── .buckconfig.d/
│ └── common.buckconfig
├── .buckroot
├── .devcontainer/
│ ├── Containerfile
│ └── devcontainer.json
├── .github/
│ ├── dependabot.yml
│ ├── scripts/
│ │ ├── bad_targets
│ │ └── buck_build_and_test.sh
│ └── workflows/
│ ├── TagIt.yml
│ ├── devcontainer.yml
│ ├── getdeps_linux.yml
│ ├── getdeps_mac.yml
│ ├── getdeps_shared-libs_linux.yml
│ ├── getdeps_windows.yml
│ └── oss-build-and-test.yml
├── .gitignore
├── .projectid
├── BUCK
├── CMake/
│ ├── FindCython.cmake
│ ├── FindFastFloat.cmake
│ ├── FindFmt.cmake
│ ├── FindLZ4.cmake
│ ├── FindLibAIO.cmake
│ ├── FindLibDwarf.cmake
│ ├── FindLibUring.cmake
│ ├── FindLibiberty.cmake
│ ├── FindLibsodium.cmake
│ ├── FindSnappy.cmake
│ ├── FindZstd.cmake
│ ├── FollyCompilerMSVC.cmake
│ ├── FollyCompilerUnix.cmake
│ ├── FollyConfigChecks.cmake
│ ├── FollyFunctions.cmake
│ ├── GenPkgConfig.cmake
│ ├── folly-config.cmake.in
│ ├── folly-config.h.cmake
│ ├── folly-deps.cmake
│ └── libfolly.pc.in
├── CMakeLists.txt
├── CMakeListsForBuck2.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── PACKAGE
├── README.md
├── buck2
├── build/
│ ├── buck2/
│ │ ├── README.md
│ │ └── install_deps/
│ │ ├── BUCK
│ │ ├── install_deps.sh
│ │ └── repos/
│ │ ├── fedora
│ │ ├── homebrew
│ │ └── ubuntu
│ └── fbcode_builder/
│ ├── .gitignore
│ ├── CMake/
│ │ ├── FBBuildOptions.cmake
│ │ ├── FBCMakeParseArgs.cmake
│ │ ├── FBCompilerSettings.cmake
│ │ ├── FBCompilerSettingsMSVC.cmake
│ │ ├── FBCompilerSettingsUnix.cmake
│ │ ├── FBPythonBinary.cmake
│ │ ├── FBPythonTestAddTests.cmake
│ │ ├── FBThriftCppLibrary.cmake
│ │ ├── FBThriftLibrary.cmake
│ │ ├── FBThriftPyLibrary.cmake
│ │ ├── FindCares.cmake
│ │ ├── FindDoubleConversion.cmake
│ │ ├── FindGMock.cmake
│ │ ├── FindGflags.cmake
│ │ ├── FindGlog.cmake
│ │ ├── FindLMDB.cmake
│ │ ├── FindLibEvent.cmake
│ │ ├── FindLibUnwind.cmake
│ │ ├── FindLibiberty.cmake
│ │ ├── FindPCRE.cmake
│ │ ├── FindPCRE2.cmake
│ │ ├── FindRe2.cmake
│ │ ├── FindSodium.cmake
│ │ ├── FindXxhash.cmake
│ │ ├── FindZstd.cmake
│ │ ├── Findibverbs.cmake
│ │ ├── RustStaticLibrary.cmake
│ │ ├── fb_py_test_main.py
│ │ ├── fb_py_win_main.c
│ │ └── make_fbpy_archive.py
│ ├── LICENSE
│ ├── README.md
│ ├── getdeps/
│ │ ├── __init__.py
│ │ ├── builder.py
│ │ ├── buildopts.py
│ │ ├── cache.py
│ │ ├── cargo.py
│ │ ├── copytree.py
│ │ ├── dyndeps.py
│ │ ├── envfuncs.py
│ │ ├── errors.py
│ │ ├── expr.py
│ │ ├── fetcher.py
│ │ ├── include_rewriter.py
│ │ ├── load.py
│ │ ├── manifest.py
│ │ ├── platform.py
│ │ ├── py_wheel_builder.py
│ │ ├── runcmd.py
│ │ ├── subcmd.py
│ │ └── test/
│ │ ├── expr_test.py
│ │ ├── fixtures/
│ │ │ └── duplicate/
│ │ │ ├── foo
│ │ │ └── subdir/
│ │ │ └── foo
│ │ ├── manifest_test.py
│ │ ├── platform_test.py
│ │ ├── retry_test.py
│ │ ├── scratch_test.py
│ │ └── strip_marker_test.py
│ ├── getdeps.py
│ ├── manifests/
│ │ ├── CLI11
│ │ ├── autoconf
│ │ ├── automake
│ │ ├── benchmark
│ │ ├── blake3
│ │ ├── boost
│ │ ├── boost-python
│ │ ├── bz2
│ │ ├── c-ares
│ │ ├── cabal
│ │ ├── cachelib
│ │ ├── cinderx-3_14
│ │ ├── cinderx-main
│ │ ├── clang
│ │ ├── clang19
│ │ ├── cmake
│ │ ├── cpptoml
│ │ ├── double-conversion
│ │ ├── double-conversion-python
│ │ ├── eden
│ │ ├── edencommon
│ │ ├── exprtk
│ │ ├── fast_float
│ │ ├── fatal
│ │ ├── fb303
│ │ ├── fboss
│ │ ├── fbthrift
│ │ ├── fbthrift-python
│ │ ├── fizz
│ │ ├── fizz-python
│ │ ├── fmt
│ │ ├── fmt-python
│ │ ├── folly
│ │ ├── folly-python
│ │ ├── gcc12
│ │ ├── gcc14
│ │ ├── gflags
│ │ ├── ghc
│ │ ├── git-lfs
│ │ ├── glean
│ │ ├── glog
│ │ ├── googletest
│ │ ├── gperf
│ │ ├── hexdump
│ │ ├── hsthrift
│ │ ├── iproute2
│ │ ├── jom
│ │ ├── jq
│ │ ├── katran
│ │ ├── libaio
│ │ ├── libaio-python
│ │ ├── libbpf
│ │ ├── libcurl
│ │ ├── libdwarf
│ │ ├── libdwarf-python
│ │ ├── libelf
│ │ ├── libevent
│ │ ├── libevent-python
│ │ ├── libffi
│ │ ├── libgit2
│ │ ├── libgpiod
│ │ ├── libiberty
│ │ ├── libiberty-python
│ │ ├── libibverbs
│ │ ├── libmnl
│ │ ├── libnl
│ │ ├── liboqs
│ │ ├── libsai
│ │ ├── libsodium
│ │ ├── libunwind
│ │ ├── libusb
│ │ ├── libyaml
│ │ ├── llvm
│ │ ├── lmdb
│ │ ├── lz4
│ │ ├── lz4-python
│ │ ├── magic_enum
│ │ ├── mcrouter
│ │ ├── mononoke
│ │ ├── mononoke_integration
│ │ ├── moxygen
│ │ ├── mvfst
│ │ ├── mvfst-python
│ │ ├── ncurses
│ │ ├── nghttp2
│ │ ├── ninja
│ │ ├── nlohmann-json
│ │ ├── nmap
│ │ ├── numa
│ │ ├── openr
│ │ ├── openssl
│ │ ├── osxfuse
│ │ ├── patchelf
│ │ ├── pcre2
│ │ ├── perl
│ │ ├── pexpect
│ │ ├── proxygen
│ │ ├── python
│ │ ├── python-3_14
│ │ ├── python-click
│ │ ├── python-filelock
│ │ ├── python-main
│ │ ├── python-psutil
│ │ ├── python-ptyprocess
│ │ ├── python-pyyaml
│ │ ├── python-setuptools
│ │ ├── python-setuptools-69
│ │ ├── python-six
│ │ ├── python-toml
│ │ ├── ragel
│ │ ├── range-v3
│ │ ├── rdma-core
│ │ ├── re2
│ │ ├── rebalancer
│ │ ├── ripgrep
│ │ ├── rocksdb
│ │ ├── rust-shed
│ │ ├── sapling
│ │ ├── snappy
│ │ ├── sparsemap
│ │ ├── sqlite3
│ │ ├── systemd
│ │ ├── tabulate
│ │ ├── tree
│ │ ├── wangle
│ │ ├── wangle-python
│ │ ├── watchman
│ │ ├── xxhash
│ │ ├── xz
│ │ ├── yaml-cpp
│ │ ├── zlib
│ │ ├── zlib-python
│ │ ├── zstd
│ │ └── zstd-python
│ └── patches/
│ ├── boost_1_83_0.patch
│ ├── iproute2_oss.patch
│ ├── libiberty_install_pic_lib.patch
│ └── zlib_dont_build_more_than_needed.patch
├── build.bat
├── build.sh
└── folly/
├── .clang-format
├── AtomicHashArray-inl.h
├── AtomicHashArray.h
├── AtomicHashMap-inl.h
├── AtomicHashMap.h
├── AtomicIntrusiveLinkedList.h
├── AtomicLinkedList.h
├── AtomicUnorderedMap.h
├── BUCK
├── BUILD_MODE.bzl
├── Benchmark.cpp
├── Benchmark.h
├── BenchmarkUtil.cpp
├── BenchmarkUtil.h
├── Bits.h
├── CMakeLists.txt
├── CODING_GUIDELINES.md
├── CPortability.h
├── CancellationToken-inl.h
├── CancellationToken.cpp
├── CancellationToken.h
├── Chrono.h
├── ClockGettimeWrappers.cpp
├── ClockGettimeWrappers.h
├── ConcurrentBitSet.h
├── ConcurrentLazy.h
├── ConcurrentSkipList-inl.h
├── ConcurrentSkipList.h
├── ConstexprMath.h
├── ConstructorCallbackList.h
├── Conv.cpp
├── Conv.h
├── CppAttributes.h
├── CpuId.h
├── DefaultKeepAliveExecutor.h
├── Demangle.cpp
├── Demangle.h
├── DiscriminatedPtr.h
├── DynamicConverter.h
├── Exception.h
├── ExceptionString.cpp
├── ExceptionString.h
├── ExceptionWrapper-inl.h
├── ExceptionWrapper.cpp
├── ExceptionWrapper.h
├── Executor.cpp
├── Executor.h
├── Expected.h
├── FBString.h
├── FBVector.h
├── File.cpp
├── File.h
├── FileUtil.cpp
├── FileUtil.h
├── Fingerprint.cpp
├── Fingerprint.h
├── FixedString.h
├── FmtUtility.cpp
├── FmtUtility.h
├── FollyMemcpy.cpp
├── FollyMemcpy.h
├── FollyMemset.cpp
├── FollyMemset.h
├── Format-inl.h
├── Format.cpp
├── Format.h
├── FormatArg.h
├── FormatTraits.h
├── Function.h
├── GLog.h
├── GroupVarint.cpp
├── GroupVarint.h
├── Hash.h
├── IPAddress.cpp
├── IPAddress.h
├── IPAddressException.h
├── IPAddressV4.cpp
├── IPAddressV4.h
├── IPAddressV6.cpp
├── IPAddressV6.h
├── Indestructible.h
├── IndexedMemPool.h
├── IntrusiveList.h
├── Lazy.h
├── Likely.h
├── MPMCPipeline.h
├── MPMCQueue.h
├── MacAddress.cpp
├── MacAddress.h
├── MapUtil.h
├── Math.h
├── MaybeManagedPtr.h
├── Memory.h
├── MicroLock.cpp
├── MicroLock.h
├── MicroSpinLock.h
├── MoveWrapper.h
├── ObserverContainer.h
├── OperationCancelled.h
├── Optional.h
├── Overload.h
├── PackedSyncPtr.h
├── Padded.h
├── Poly-inl.h
├── Poly.h
├── PolyException.h
├── Portability.h
├── Preprocessor.h
├── ProducerConsumerQueue.h
├── RWSpinLock.h
├── Random-inl.h
├── Random.cpp
├── Random.h
├── Range.h
├── Replaceable.h
├── ScopeGuard.cpp
├── ScopeGuard.h
├── SharedMutex.cpp
├── SharedMutex.h
├── Singleton-inl.h
├── Singleton.cpp
├── Singleton.h
├── SingletonThreadLocal.cpp
├── SingletonThreadLocal.h
├── SocketAddress.cpp
├── SocketAddress.h
├── SpinLock.h
├── String-inl.h
├── String.cpp
├── String.h
├── Subprocess.cpp
├── Subprocess.h
├── Synchronized.h
├── SynchronizedPtr.h
├── ThreadCachedInt.h
├── ThreadLocal.h
├── TimeoutQueue.cpp
├── TimeoutQueue.h
├── TokenBucket.h
├── Traits.h
├── Try-inl.h
├── Try.cpp
├── Try.h
├── UTF8String.h
├── Unicode.cpp
├── Unicode.h
├── Unit.h
├── Uri-inl.h
├── Uri.cpp
├── Uri.h
├── Utility.h
├── VERSION
├── Varint.h
├── VirtualExecutor.h
├── algorithm/
│ ├── BUCK
│ ├── BinaryHeap.h
│ ├── CMakeLists.txt
│ ├── StableRadixSort.h
│ ├── simd/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Contains.cpp
│ │ ├── Contains.h
│ │ ├── FindFixed.h
│ │ ├── Ignore.h
│ │ ├── Movemask.h
│ │ ├── detail/
│ │ │ ├── BUCK
│ │ │ ├── CMakeLists.txt
│ │ │ ├── ContainsImpl.h
│ │ │ ├── SimdAnyOf.h
│ │ │ ├── SimdForEach.h
│ │ │ ├── SimdPlatform.h
│ │ │ ├── Traits.h
│ │ │ ├── UnrollUtils.h
│ │ │ └── test/
│ │ │ ├── BUCK
│ │ │ ├── SimdAnyOfTest.cpp
│ │ │ ├── SimdForEachTest.cpp
│ │ │ ├── TraitsTest.cpp
│ │ │ └── UnrollUtilsTest.cpp
│ │ ├── find_first_of.h
│ │ ├── find_first_of_extra.h
│ │ └── test/
│ │ ├── BUCK
│ │ ├── ContainsTest.cpp
│ │ ├── FindFixedBenchmark.cpp
│ │ ├── FindFixedTest.cpp
│ │ ├── MovemaskTest.cpp
│ │ ├── find_first_of_bench.cpp
│ │ └── find_first_of_test.cpp
│ └── test/
│ ├── BUCK
│ ├── BinaryHeapTest.cpp
│ ├── StableRadixSortBenchmark.cpp
│ └── StableRadixSortTest.cpp
├── base64.h
├── buck_config/
│ ├── BUCK
│ └── constraints/
│ └── BUCK
├── build/
│ ├── bootstrap-osx-homebrew.sh
│ └── build-debs-ubuntu-18.04.sh
├── channels/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Channel-fwd.h
│ ├── Channel-inl.h
│ ├── Channel.h
│ ├── ChannelCallbackHandle.h
│ ├── ChannelProcessor-inl.h
│ ├── ChannelProcessor.h
│ ├── ConsumeChannel-inl.h
│ ├── ConsumeChannel.h
│ ├── FanoutChannel-inl.h
│ ├── FanoutChannel.h
│ ├── FanoutSender-inl.h
│ ├── FanoutSender.h
│ ├── MaxConcurrentRateLimiter.cpp
│ ├── MaxConcurrentRateLimiter.h
│ ├── Merge-inl.h
│ ├── Merge.h
│ ├── MergeChannel-inl.h
│ ├── MergeChannel.h
│ ├── MultiplexChannel-inl.h
│ ├── MultiplexChannel.h
│ ├── OnClosedException.h
│ ├── Producer-inl.h
│ ├── Producer.h
│ ├── ProxyChannel-inl.h
│ ├── ProxyChannel.h
│ ├── RateLimiter.h
│ ├── Transform-inl.h
│ ├── Transform.h
│ ├── detail/
│ │ ├── AtomicQueue.h
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── ChannelBridge.h
│ │ ├── IntrusivePtr.h
│ │ ├── MultiplexerTraits.h
│ │ ├── PointerVariant.h
│ │ ├── Utility.h
│ │ └── test/
│ │ ├── AtomicQueueTest.cpp
│ │ ├── BUCK
│ │ └── PointerVariantTest.cpp
│ └── test/
│ ├── BUCK
│ ├── ChannelProcessorTest.cpp
│ ├── ChannelTest.cpp
│ ├── ChannelTestUtil.h
│ ├── FanoutChannelTest.cpp
│ ├── FanoutSenderTest.cpp
│ ├── MaxConcurrentRateLimiterTest.cpp
│ ├── MergeChannelTest.cpp
│ ├── MergeTest.cpp
│ ├── MultiplexChannelTest.cpp
│ ├── ProducerTest.cpp
│ ├── ProxyChannelTest.cpp
│ └── TransformTest.cpp
├── chrono/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Clock.h
│ ├── Conv.h
│ ├── Hardware.h
│ └── test/
│ ├── BUCK
│ ├── ClockTest.cpp
│ └── ConvTest.cpp
├── cli/
│ ├── Args.cpp
│ ├── Args.h
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── NestedCommandLineApp.cpp
│ ├── NestedCommandLineApp.h
│ ├── ProgramOptions.cpp
│ ├── ProgramOptions.h
│ └── test/
│ ├── ArgsTest.cpp
│ ├── BUCK
│ ├── NestedCommandLineAppExample.cpp
│ ├── NestedCommandLineAppTest.cpp
│ ├── NestedCommandLineAppTestHelper.cpp
│ ├── ProgramOptionsTest.cpp
│ └── ProgramOptionsTestHelper.cpp
├── codec/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Uuid.h
│ ├── hex.h
│ └── test/
│ ├── BUCK
│ ├── UuidBenchmark.cpp
│ ├── UuidTest.cpp
│ └── hex_test.cpp
├── compression/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Compression.cpp
│ ├── Compression.h
│ ├── CompressionContextPool.h
│ ├── CompressionContextPoolSingletons.cpp
│ ├── CompressionContextPoolSingletons.h
│ ├── CompressionCoreLocalContextPool.h
│ ├── Instructions.h
│ ├── QuotientMultiSet-inl.h
│ ├── QuotientMultiSet.cpp
│ ├── QuotientMultiSet.h
│ ├── Select64.cpp
│ ├── Select64.h
│ ├── Utils.h
│ ├── Zlib.cpp
│ ├── Zlib.h
│ ├── Zstd.cpp
│ ├── Zstd.h
│ ├── elias_fano/
│ │ ├── BUCK
│ │ ├── BitVectorCoding.h
│ │ ├── CMakeLists.txt
│ │ ├── CodingDetail.h
│ │ ├── EliasFanoCoding.h
│ │ └── test/
│ │ ├── BUCK
│ │ ├── BitVectorCodingTest.cpp
│ │ └── EliasFanoCodingTest.cpp
│ └── test/
│ ├── BUCK
│ ├── CodingTestUtils.cpp
│ ├── CodingTestUtils.h
│ ├── CompressionContextPoolBenchmark.cpp
│ ├── CompressionContextPoolTest.cpp
│ ├── CompressionTest.cpp
│ ├── InstructionsTest.cpp
│ ├── LargeTest.cpp
│ ├── QuotientMultiSetBenchmark.cpp
│ ├── QuotientMultiSetTest.cpp
│ └── Select64Test.cpp
├── concurrency/
│ ├── AtomicSharedPtr.h
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── CacheLocality.cpp
│ ├── CacheLocality.h
│ ├── ConcurrentHashMap.h
│ ├── CoreCachedSharedPtr.h
│ ├── DeadlockDetector.cpp
│ ├── DeadlockDetector.h
│ ├── DynamicBoundedQueue.h
│ ├── PriorityUnboundedQueueSet.h
│ ├── ProcessLocalUniqueId.cpp
│ ├── ProcessLocalUniqueId.h
│ ├── SingletonRelaxedCounter.h
│ ├── ThreadCachedSynchronized.h
│ ├── UnboundedQueue.h
│ ├── container/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── FlatCombiningPriorityQueue.h
│ │ ├── LockFreeRingBuffer.h
│ │ ├── RelaxedConcurrentPriorityQueue.h
│ │ ├── SingleWriterFixedHashMap.h
│ │ ├── atomic_grow_array.h
│ │ └── test/
│ │ ├── BUCK
│ │ ├── FlatCombiningPriorityQueueTest.cpp
│ │ ├── LockFreeRingBufferTest.cpp
│ │ ├── RelaxedConcurrentPriorityQueueTest.cpp
│ │ ├── SingleWriterFixedHashMapTest.cpp
│ │ └── atomic_grow_array_test.cpp
│ ├── detail/
│ │ ├── AtomicSharedPtr-detail.h
│ │ └── ConcurrentHashMap-detail.h
│ ├── memory/
│ │ ├── AtomicReadMostlyMainPtr.cpp
│ │ ├── AtomicReadMostlyMainPtr.h
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── PrimaryPtr.h
│ │ ├── ReadMostlySharedPtr.h
│ │ ├── TLRefCount.h
│ │ └── test/
│ │ ├── AtomicReadMostlyMainPtrBenchmark.cpp
│ │ ├── AtomicReadMostlyMainPtrTest.cpp
│ │ ├── BUCK
│ │ ├── PrimaryPtrTest.cpp
│ │ ├── ReadMostlySharedPtrBenchmark.cpp
│ │ ├── ReadMostlySharedPtrStressTest.cpp
│ │ ├── ReadMostlySharedPtrTest.cpp
│ │ ├── RefCountBenchmark.cpp
│ │ └── RefCountTest.cpp
│ └── test/
│ ├── AtomicSharedPtrCounted.h
│ ├── AtomicSharedPtrPerformance.cpp
│ ├── AtomicSharedPtrTest.cpp
│ ├── BUCK
│ ├── CacheLocalityBenchmark.cpp
│ ├── CacheLocalityInitMain.cpp
│ ├── CacheLocalityTest.cpp
│ ├── ConcurrentHashMapBench.cpp
│ ├── ConcurrentHashMapStressTest.cpp
│ ├── ConcurrentHashMapTest.cpp
│ ├── CoreCachedSharedPtrTest.cpp
│ ├── DynamicBoundedQueueTest.cpp
│ ├── PriorityUnboundedQueueSetTest.cpp
│ ├── ProcessLocalUniqueIdTest.cpp
│ ├── SingletonRelaxedCounterBench.cpp
│ ├── SingletonRelaxedCounterTest.cpp
│ ├── ThreadCachedSynchronizedBench.cpp
│ ├── ThreadCachedSynchronizedTest.cpp
│ └── UnboundedQueueTest.cpp
├── container/
│ ├── Access.h
│ ├── Array.h
│ ├── BUCK
│ ├── BitIterator.h
│ ├── CMakeLists.txt
│ ├── CollectionUtil.h
│ ├── Enumerate.h
│ ├── EvictingCacheMap.h
│ ├── F14.md
│ ├── F14Map-fwd.h
│ ├── F14Map.h
│ ├── F14Set-fwd.h
│ ├── F14Set.h
│ ├── FBVector.h
│ ├── Foreach-inl.h
│ ├── Foreach.h
│ ├── HeterogeneousAccess-fwd.h
│ ├── HeterogeneousAccess.h
│ ├── IntrusiveHeap.h
│ ├── IntrusiveList.h
│ ├── Iterator.h
│ ├── MapUtil.h
│ ├── Merge.h
│ ├── RegexMatchCache.cpp
│ ├── RegexMatchCache.h
│ ├── Reserve.h
│ ├── SparseByteSet.h
│ ├── StdBitset.h
│ ├── View.h
│ ├── WeightedEvictingCacheMap.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── BitIteratorDetail.h
│ │ ├── BoolWrapper.h
│ │ ├── CMakeLists.txt
│ │ ├── F14Defaults.h
│ │ ├── F14IntrinsicsAvailability.h
│ │ ├── F14MapFallback.h
│ │ ├── F14Mask.h
│ │ ├── F14Policy.h
│ │ ├── F14SetFallback.h
│ │ ├── F14Table.cpp
│ │ ├── F14Table.h
│ │ ├── Util.h
│ │ ├── tape_detail.h
│ │ └── test/
│ │ ├── BUCK
│ │ ├── BoolWrapperTest.cpp
│ │ └── F14DetailTest.cpp
│ ├── heap_vector_types.h
│ ├── irange.h
│ ├── range_traits.h
│ ├── small_vector.h
│ ├── sorted_vector_types.h
│ ├── span.h
│ ├── tape.h
│ ├── test/
│ │ ├── .clang-tidy
│ │ ├── AccessTest.cpp
│ │ ├── ArrayTest.cpp
│ │ ├── BUCK
│ │ ├── BitIteratorBench.cpp
│ │ ├── BitIteratorTest.cpp
│ │ ├── CollectionUtilTest.cpp
│ │ ├── EnumerateTest.cpp
│ │ ├── EvictingCacheMapBench.cpp
│ │ ├── EvictingCacheMapTest.cpp
│ │ ├── F14AsanSupportTest.cpp
│ │ ├── F14FwdTest.cpp
│ │ ├── F14InterprocessTest.cpp
│ │ ├── F14MapTest.cpp
│ │ ├── F14PolicyTest.cpp
│ │ ├── F14SetTest.cpp
│ │ ├── F14SmallOverheads.cpp
│ │ ├── F14TestUtil.h
│ │ ├── FBVectorBenchmark.cpp
│ │ ├── FBVectorBenchmarks.cpp.h
│ │ ├── FBVectorTest.cpp
│ │ ├── FBVectorTests.cpp.h
│ │ ├── ForeachBenchmark.cpp
│ │ ├── ForeachTest.cpp
│ │ ├── HashMapsBench.cpp
│ │ ├── HeterogeneousAccessTest.cpp
│ │ ├── IRangeTest.cpp
│ │ ├── IntrusiveHeapTest.cpp
│ │ ├── IteratorTest.cpp
│ │ ├── MapUtilTest.cpp
│ │ ├── MergeTest.cpp
│ │ ├── RegexMatchCacheTest.cpp
│ │ ├── ReserveTest.cpp
│ │ ├── SparseByteSetBenchmark.cpp
│ │ ├── SparseByteSetTest.cpp
│ │ ├── StdBitsetBenchmark.cpp
│ │ ├── StdBitsetTest.cpp
│ │ ├── TrackingTypes.h
│ │ ├── UtilTest.cpp
│ │ ├── WeightedEvictingCacheMapTest.cpp
│ │ ├── heap_vector_types_test.cpp
│ │ ├── range_traits_test.cpp
│ │ ├── small_vector_test.cpp
│ │ ├── sorted_vector_test.cpp
│ │ ├── span_test.cpp
│ │ ├── tape_bench.cpp
│ │ ├── tape_test.cpp
│ │ └── vector_bool_test.cpp
│ └── vector_bool.h
├── coro/
│ ├── Accumulate-inl.h
│ ├── Accumulate.h
│ ├── AsyncGenerator.h
│ ├── AsyncPipe.h
│ ├── AsyncScope.h
│ ├── AsyncStack.h
│ ├── AutoCleanup-fwd.h
│ ├── AutoCleanup.h
│ ├── BUCK
│ ├── BasePromise.h
│ ├── Baton.cpp
│ ├── Baton.h
│ ├── BlockingWait.h
│ ├── BoundedQueue.h
│ ├── CMakeLists.txt
│ ├── Cleanup.h
│ ├── Collect-inl.h
│ ├── Collect.h
│ ├── Concat-inl.h
│ ├── Concat.h
│ ├── Coroutine.h
│ ├── CurrentExecutor.h
│ ├── DetachOnCancel.h
│ ├── Error.h
│ ├── Filter-inl.h
│ ├── Filter.h
│ ├── FutureUtil.h
│ ├── Generator.h
│ ├── GmockHelpers.h
│ ├── GtestHelpers.h
│ ├── Invoke.h
│ ├── Merge-inl.h
│ ├── Merge.h
│ ├── Mutex.cpp
│ ├── Mutex.h
│ ├── Nothrow.h
│ ├── Promise.h
│ ├── README.md
│ ├── Result.h
│ ├── Retry.h
│ ├── RustAdaptors.h
│ ├── ScopeExit.h
│ ├── SerialQueueRunner.cpp
│ ├── SerialQueueRunner.h
│ ├── SharedLock.h
│ ├── SharedMutex.cpp
│ ├── SharedMutex.h
│ ├── SharedPromise.h
│ ├── Sleep-inl.h
│ ├── Sleep.h
│ ├── SmallUnboundedQueue.h
│ ├── Synchronized.h
│ ├── Task.h
│ ├── TaskWrapper.h
│ ├── TimedWait.h
│ ├── Timeout-inl.h
│ ├── Timeout.h
│ ├── Traits.h
│ ├── Transform-inl.h
│ ├── Transform.h
│ ├── UnboundedQueue.h
│ ├── ValueOrError.h
│ ├── ValueOrFatal.h
│ ├── ViaIfAsync.h
│ ├── WithAsyncStack.h
│ ├── WithCancellation.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── Barrier.h
│ │ ├── BarrierTask.h
│ │ ├── CMakeLists.txt
│ │ ├── CurrentAsyncFrame.h
│ │ ├── Helpers.h
│ │ ├── InlineTask.h
│ │ ├── Malloc.cpp
│ │ ├── Malloc.h
│ │ ├── ManualLifetime.h
│ │ ├── PickTaskWrapper.h
│ │ ├── Traits.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── PickTaskWrapperTest.cpp
│ ├── safe/
│ │ ├── AsyncClosure-fwd.h
│ │ ├── AsyncClosure.h
│ │ ├── BUCK
│ │ ├── BindCaptures.h
│ │ ├── CMakeLists.txt
│ │ ├── Captures.h
│ │ ├── NowTask.h
│ │ ├── SafeTask.h
│ │ ├── detail/
│ │ │ ├── AsyncClosure.h
│ │ │ ├── BUCK
│ │ │ ├── BindAsyncClosure.h
│ │ │ ├── CMakeLists.txt
│ │ │ ├── DefineMovableDeepConstLrefCopyable.h
│ │ │ └── test/
│ │ │ ├── BUCK
│ │ │ ├── BindAsyncClosureTest.cpp
│ │ │ └── DefineMovableDeepConstLrefCopyableTest.cpp
│ │ ├── docs/
│ │ │ ├── AsUnsafe.md
│ │ │ └── Captures.md
│ │ └── test/
│ │ ├── AsyncClosureTest.cpp
│ │ ├── BUCK
│ │ ├── CapturesTest.cpp
│ │ ├── NowTaskTest.cpp
│ │ └── SafeTaskTest.cpp
│ ├── scripts/
│ │ ├── BUCK
│ │ ├── __init__.py
│ │ ├── co_bt.py
│ │ └── test/
│ │ ├── BUCK
│ │ └── co_bt.py
│ └── test/
│ ├── AccumulateTest.cpp
│ ├── AsyncGeneratorBenchmark.cpp
│ ├── AsyncGeneratorTest.cpp
│ ├── AsyncPipeTest.cpp
│ ├── AsyncScopeTest.cpp
│ ├── AsyncStackTest.cpp
│ ├── BUCK
│ ├── BatonTest.cpp
│ ├── BlockingWaitBenchmark.cpp
│ ├── BlockingWaitTest.cpp
│ ├── BoundedQueueTest.cpp
│ ├── CollectAllBenchmark.cpp
│ ├── CollectAllTryBenchmark.cpp
│ ├── CollectTest.cpp
│ ├── ConcatTest.cpp
│ ├── CoroBenchmarkAllocator.cpp
│ ├── CoroBenchmarkNRVO.cpp
│ ├── CoroTest.cpp
│ ├── CoroutineTest.cpp
│ ├── CurrentExecutorTest.cpp
│ ├── ErrorTest.cpp
│ ├── FilterTest.cpp
│ ├── FutureUtilTest.cpp
│ ├── GeneratorTest.cpp
│ ├── GmockHelpersTest.cpp
│ ├── GtestHelpersTest.cpp
│ ├── InlineTaskTest.cpp
│ ├── MergeTest.cpp
│ ├── MutexTest.cpp
│ ├── PromiseBenchmark.cpp
│ ├── PromiseTest.cpp
│ ├── RequestContextTest.cpp
│ ├── RetryTest.cpp
│ ├── RustAdaptorsTest.cpp
│ ├── ScopeExitTest.cpp
│ ├── SerialQueueRunnerTest.cpp
│ ├── SharedMutexBenchmark.cpp
│ ├── SharedMutexTest.cpp
│ ├── SharedPromiseTest.cpp
│ ├── SleepTest.cpp
│ ├── SmallUnboundedQueueTest.cpp
│ ├── SuspendedStackTest.cpp
│ ├── SynchronizedTest.cpp
│ ├── TaskBenchmark.cpp
│ ├── TaskTest.cpp
│ ├── TaskWrapperTest.cpp
│ ├── TimeoutTest.cpp
│ ├── TraitsTest.cpp
│ ├── TransformTest.cpp
│ ├── UnboundedQueueTest.cpp
│ ├── ValueOrErrorTest.cpp
│ ├── ValueOrFatalTest.cpp
│ └── WithAsyncStackTest.cpp
├── crypto/
│ ├── BUCK
│ ├── Blake2xb.cpp
│ ├── Blake2xb.h
│ ├── CMakeLists.txt
│ ├── LtHash-inl.h
│ ├── LtHash.cpp
│ ├── LtHash.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── LtHashInternal.h
│ │ ├── MathOperation_AVX2.cpp
│ │ ├── MathOperation_SSE2.cpp
│ │ └── MathOperation_Simple.cpp
│ └── test/
│ ├── BUCK
│ ├── Blake2xbBenchmark.cpp
│ ├── Blake2xbTest.cpp
│ ├── LtHashBenchmark.cpp
│ └── LtHashTest.cpp
├── debugging/
│ ├── CMakeLists.txt
│ ├── exception_tracer/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Compatibility.h
│ │ ├── ExceptionAbi.h
│ │ ├── ExceptionCounterLib.cpp
│ │ ├── ExceptionCounterLib.h
│ │ ├── ExceptionCounterLibStaticRegistration.cpp
│ │ ├── ExceptionStackTraceLib.cpp
│ │ ├── ExceptionTracer.cpp
│ │ ├── ExceptionTracer.h
│ │ ├── ExceptionTracerLib.cpp
│ │ ├── ExceptionTracerLib.h
│ │ ├── README.md
│ │ ├── SmartExceptionStackTraceHooks.cpp
│ │ ├── SmartExceptionTracer.cpp
│ │ ├── SmartExceptionTracer.h
│ │ ├── SmartExceptionTracerSingleton.cpp
│ │ ├── SmartExceptionTracerSingleton.h
│ │ ├── StackTrace.cpp
│ │ ├── StackTrace.h
│ │ └── test/
│ │ ├── BUCK
│ │ ├── ExceptionCounterTest.cpp
│ │ ├── ExceptionTracerBenchmark.cpp
│ │ ├── ExceptionTracerTest.cpp
│ │ ├── ExceptionTracerUncaughtTest.cpp
│ │ ├── SmartExceptionTracerBenchmark.cpp
│ │ ├── SmartExceptionTracerTest.cpp
│ │ └── exception_tracer_uncaught_test.py
│ └── symbolizer/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Dwarf.cpp
│ ├── Dwarf.h
│ ├── DwarfImpl.cpp
│ ├── DwarfImpl.h
│ ├── DwarfLineNumberVM.cpp
│ ├── DwarfLineNumberVM.h
│ ├── DwarfSection.cpp
│ ├── DwarfSection.h
│ ├── DwarfUtil.cpp
│ ├── DwarfUtil.h
│ ├── Elf-inl.h
│ ├── Elf.cpp
│ ├── Elf.h
│ ├── ElfCache.cpp
│ ├── ElfCache.h
│ ├── LineReader.cpp
│ ├── LineReader.h
│ ├── SignalHandler.cpp
│ ├── SignalHandler.h
│ ├── StackTrace.cpp
│ ├── StackTrace.h
│ ├── SymbolizePrinter.cpp
│ ├── SymbolizePrinter.h
│ ├── SymbolizedFrame.cpp
│ ├── SymbolizedFrame.h
│ ├── Symbolizer.cpp
│ ├── Symbolizer.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Debug.cpp
│ │ └── Debug.h
│ ├── test/
│ │ ├── BUCK
│ │ ├── Crash.cpp
│ │ ├── DwarfBenchmark.cpp
│ │ ├── ElfTest.cpp
│ │ ├── LineReaderTest.cpp
│ │ ├── SignalHandlerTest.cpp
│ │ ├── SignalHandlerTest.h
│ │ ├── SimpleElf.cpp
│ │ ├── SmallSigAltStackCrash.cpp
│ │ ├── StackTraceBenchmark.cpp
│ │ ├── StackTraceSizeLimitTest.cpp
│ │ ├── StackTraceTest.cpp
│ │ ├── SymbolizedFrameTest.cpp
│ │ ├── SymbolizerTest.cpp
│ │ ├── SymbolizerTestUtils-inl.h
│ │ ├── SymbolizerTestUtils.cpp
│ │ ├── SymbolizerTestUtils.h
│ │ ├── compare-addr2line.sh
│ │ ├── gnu_debuglink_test.sh
│ │ ├── symbolizer_dwp_compability.sh
│ │ ├── tool/
│ │ │ ├── BUCK
│ │ │ └── segv.cpp
│ │ └── validate_folly_symbolizer.bzl
│ └── tool/
│ ├── Addr2Line.cpp
│ ├── BUCK
│ ├── LibSegFault.cpp
│ └── libFollySegFault.so.v
├── defs.bzl
├── detail/
│ ├── AsyncTrace.cpp
│ ├── AsyncTrace.h
│ ├── AtomicHashUtils.h
│ ├── AtomicUnorderedMapUtils.h
│ ├── Avx2.cpp
│ ├── Avx2.h
│ ├── BUCK
│ ├── BenchmarkAdaptive.cpp
│ ├── BenchmarkAdaptive.h
│ ├── CMakeLists.txt
│ ├── DiscriminatedPtrDetail.h
│ ├── FileUtilDetail.cpp
│ ├── FileUtilDetail.h
│ ├── FileUtilVectorDetail.h
│ ├── FingerprintPolynomial.h
│ ├── Futex-inl.h
│ ├── Futex.cpp
│ ├── Futex.h
│ ├── GroupVarintDetail.h
│ ├── IPAddress.cpp
│ ├── IPAddress.h
│ ├── IPAddressSource.h
│ ├── Iterators.h
│ ├── MPMCPipelineDetail.h
│ ├── MemoryIdler.cpp
│ ├── MemoryIdler.h
│ ├── PerfScoped.cpp
│ ├── PerfScoped.h
│ ├── PolyDetail.h
│ ├── RangeCommon.cpp
│ ├── RangeCommon.h
│ ├── RangeSimd.cpp
│ ├── RangeSimd.h
│ ├── RangeSse42.cpp
│ ├── RangeSse42.h
│ ├── SimpleSimdStringUtils.cpp
│ ├── SimpleSimdStringUtils.h
│ ├── SimpleSimdStringUtilsImpl.h
│ ├── Singleton.h
│ ├── SlowFingerprint.h
│ ├── SocketFastOpen.cpp
│ ├── SocketFastOpen.h
│ ├── SplitStringSimd.h
│ ├── SplitStringSimdImpl.h
│ ├── Sse.cpp
│ ├── Sse.h
│ ├── StaticSingletonManager.cpp
│ ├── StaticSingletonManager.h
│ ├── ThreadLocalDetail.cpp
│ ├── ThreadLocalDetail.h
│ ├── TrapOnAvx512.cpp
│ ├── TrapOnAvx512.h
│ ├── TurnSequencer.h
│ ├── TypeList.h
│ ├── UniqueInstance.cpp
│ ├── UniqueInstance.h
│ ├── base64_detail/
│ │ ├── BUCK
│ │ ├── Base64Api.cpp
│ │ ├── Base64Api.h
│ │ ├── Base64Common.h
│ │ ├── Base64Constants.h
│ │ ├── Base64HiddenConstants.h
│ │ ├── Base64SWAR.cpp
│ │ ├── Base64SWAR.h
│ │ ├── Base64Scalar.h
│ │ ├── Base64Simd.h
│ │ ├── Base64_SSE4_2.cpp
│ │ ├── Base64_SSE4_2.h
│ │ ├── Base64_SSE4_2_Platform.h
│ │ ├── CMakeLists.txt
│ │ ├── README.md
│ │ └── test/
│ │ ├── BUCK
│ │ ├── Base64AgainstScalarTest.cpp
│ │ ├── Base64PlatformTest.cpp
│ │ └── Base64SpecialCasesTest.cpp
│ ├── test/
│ │ ├── AsyncTraceTest.cpp
│ │ ├── BUCK
│ │ ├── BenchmarkAdaptiveTest.cpp
│ │ ├── FileUtilDetailTest.cpp
│ │ ├── PerfScopedTest.cpp
│ │ ├── SimpleSimdStringUtilsTest.cpp
│ │ ├── SplitStringSimdTest.cpp
│ │ ├── StaticSingletonManagerTest.cpp
│ │ ├── ThreadLocalBenchmark.cpp
│ │ ├── ThreadLocalDetailTest.cpp
│ │ ├── TrapOnAvx512TestNegative.cpp
│ │ ├── TypeListTest.cpp
│ │ ├── UniqueInstanceTest.cpp
│ │ └── tuple_test.cpp
│ ├── thread_local_globals.cpp
│ ├── thread_local_globals.h
│ └── tuple.h
├── docs/
│ ├── .gitignore
│ ├── AtomicHashMap.md
│ ├── BUCK
│ ├── Benchmark.md
│ ├── BenchmarkAdaptive.md
│ ├── Conv.md
│ ├── Dynamic.md
│ ├── DynamicConverter.md
│ ├── Executors.md
│ ├── FBString.md
│ ├── FBVector.md
│ ├── Format.md
│ ├── Function.md
│ ├── Futures.md
│ ├── GroupVarint.md
│ ├── Hazptr.md
│ ├── Histogram.md
│ ├── MapUtils.md
│ ├── Overview.md
│ ├── PackedSyncPtr.md
│ ├── Poly.md
│ ├── ProducerConsumerQueue.md
│ ├── Rcu.md
│ ├── SmallLocks.md
│ ├── Switch.md
│ ├── Synchronized.md
│ ├── ThreadCachedInt.md
│ ├── ThreadLocal.md
│ ├── Traits.md
│ ├── defs.bzl
│ ├── examples/
│ │ └── folly/
│ │ ├── BUCK
│ │ ├── CancellationCallback.cpp
│ │ ├── CancellationSource.cpp
│ │ ├── CancellationToken.cpp
│ │ ├── DynamicConverter.cpp
│ │ ├── ExecutorGuide.cpp
│ │ ├── File.cpp
│ │ ├── Format.cpp
│ │ ├── Function.cpp
│ │ ├── Likely.cpp
│ │ ├── MapUtil.cpp
│ │ ├── ScopeGuard.cpp
│ │ ├── ScopeGuard2.cpp
│ │ ├── ScopedEventBaseThread.cpp
│ │ ├── ScopedEventBaseThread2.cpp
│ │ ├── Synchronized.cpp
│ │ ├── container/
│ │ │ ├── Array.cpp
│ │ │ └── BUCK
│ │ ├── coro/
│ │ │ ├── AsyncScope.cpp
│ │ │ ├── BUCK
│ │ │ ├── CancellableAsyncScope.cpp
│ │ │ ├── DetachOnCancel.cpp
│ │ │ ├── Promise.cpp
│ │ │ ├── Retry.cpp
│ │ │ ├── Task.cpp
│ │ │ └── WithCancellation.cpp
│ │ ├── dynamic/
│ │ │ ├── BUCK
│ │ │ ├── array.cpp
│ │ │ └── object.cpp
│ │ ├── dynamic.cpp
│ │ ├── hash/
│ │ │ ├── BUCK
│ │ │ └── Hash.cpp
│ │ ├── io/
│ │ │ ├── BUCK
│ │ │ └── IOBuf.cpp
│ │ ├── ipaddress.cpp
│ │ ├── synchronization/
│ │ │ └── Baton.cpp
│ │ └── test/
│ │ ├── BUCK
│ │ ├── TestUtils.cpp
│ │ └── common/
│ │ ├── BUCK
│ │ └── TestMainDemo.cpp
│ ├── small_vector.md
│ └── style.css
├── dynamic-inl.h
├── dynamic.h
├── executors/
│ ├── Async.h
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── CPUThreadPoolExecutor.cpp
│ ├── CPUThreadPoolExecutor.h
│ ├── Codel.cpp
│ ├── Codel.h
│ ├── DrivableExecutor.h
│ ├── EDFThreadPoolExecutor.cpp
│ ├── EDFThreadPoolExecutor.h
│ ├── ExecutionObserver.cpp
│ ├── ExecutionObserver.h
│ ├── ExecutorWithPriority-inl.h
│ ├── ExecutorWithPriority.cpp
│ ├── ExecutorWithPriority.h
│ ├── FiberIOExecutor.h
│ ├── FunctionScheduler.cpp
│ ├── FunctionScheduler.h
│ ├── FutureExecutor.h
│ ├── GlobalExecutor.cpp
│ ├── GlobalExecutor.h
│ ├── GlobalThreadPoolList.cpp
│ ├── GlobalThreadPoolList.h
│ ├── IOExecutor.h
│ ├── IOObjectCache.h
│ ├── IOThreadPoolDeadlockDetectorObserver.cpp
│ ├── IOThreadPoolDeadlockDetectorObserver.h
│ ├── IOThreadPoolExecutor.cpp
│ ├── IOThreadPoolExecutor.h
│ ├── InlineExecutor.cpp
│ ├── InlineExecutor.h
│ ├── ManualExecutor.cpp
│ ├── ManualExecutor.h
│ ├── MeteredExecutor-inl.h
│ ├── MeteredExecutor.h
│ ├── QueueObserver.cpp
│ ├── QueueObserver.h
│ ├── QueuedImmediateExecutor.cpp
│ ├── QueuedImmediateExecutor.h
│ ├── ScheduledExecutor.h
│ ├── SequencedExecutor.h
│ ├── SerialExecutor-inl.h
│ ├── SerialExecutor.h
│ ├── SerializedExecutor.h
│ ├── SoftRealTimeExecutor.cpp
│ ├── SoftRealTimeExecutor.h
│ ├── StrandExecutor.cpp
│ ├── StrandExecutor.h
│ ├── StripedEDFThreadPoolExecutor.cpp
│ ├── StripedEDFThreadPoolExecutor.h
│ ├── ThreadPoolExecutor.cpp
│ ├── ThreadPoolExecutor.h
│ ├── ThreadedExecutor.cpp
│ ├── ThreadedExecutor.h
│ ├── ThreadedRepeatingFunctionRunner.cpp
│ ├── ThreadedRepeatingFunctionRunner.h
│ ├── TimedDrivableExecutor.cpp
│ ├── TimedDrivableExecutor.h
│ ├── TimekeeperScheduledExecutor.cpp
│ ├── TimekeeperScheduledExecutor.h
│ ├── VirtualExecutor.h
│ ├── task_queue/
│ │ ├── BUCK
│ │ ├── BlockingQueue.h
│ │ ├── CMakeLists.txt
│ │ ├── LifoSemMPMCQueue.h
│ │ ├── PriorityLifoSemMPMCQueue.h
│ │ ├── PriorityUnboundedBlockingQueue.h
│ │ ├── StripedPriorityUnboundedBlockingQueue.h
│ │ ├── UnboundedBlockingQueue.h
│ │ └── test/
│ │ ├── BUCK
│ │ ├── PriorityLifoSemMPMCQueueTest.cpp
│ │ ├── PriorityUnboundedBlockingQueueTest.cpp
│ │ ├── StripedPriorityUnboundedBlockingQueueTest.cpp
│ │ ├── UnboundedBlockingQueueBench.cpp
│ │ └── UnboundedBlockingQueueTest.cpp
│ ├── test/
│ │ ├── AsyncTest.cpp
│ │ ├── BUCK
│ │ ├── CodelTest.cpp
│ │ ├── EDFThreadPoolExecutorBenchmark.cpp
│ │ ├── ExecutorTest.cpp
│ │ ├── ExecutorWithPriorityTest.cpp
│ │ ├── FiberIOExecutorTest.cpp
│ │ ├── FunctionSchedulerTest.cpp
│ │ ├── GlobalCPUExecutorTest.cpp
│ │ ├── GlobalExecutorAssignmentTest.cpp
│ │ ├── GlobalExecutorTest.cpp
│ │ ├── GlobalIOExecutorTest.cpp
│ │ ├── IOThreadPoolDeadlockDetectorObserverTest.cpp
│ │ ├── IOThreadPoolExecutorBaseTestLib.h
│ │ ├── IOThreadPoolExecutorTest.cpp
│ │ ├── MeteredExecutorTest.cpp
│ │ ├── SequencedExecutorTest.cpp
│ │ ├── SerialExecutorTest.cpp
│ │ ├── StrandExecutorTest.cpp
│ │ ├── StripedEDFThreadPoolExecutorTest.cpp
│ │ ├── ThreadPoolExecutorTest.cpp
│ │ ├── ThreadedExecutorTest.cpp
│ │ ├── ThreadedRepeatingFunctionRunnerTest.cpp
│ │ ├── TimedDrivableExecutorTest.cpp
│ │ └── TimekeeperScheduledExecutorTest.cpp
│ └── thread_factory/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── InitThreadFactory.h
│ ├── NamedThreadFactory.h
│ ├── PriorityThreadFactory.cpp
│ ├── PriorityThreadFactory.h
│ └── ThreadFactory.h
├── experimental/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── EventCount.h
│ ├── FlatCombiningPriorityQueue.h
│ ├── FunctionScheduler.h
│ ├── ThreadedRepeatingFunctionRunner.h
│ ├── channels/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Channel-fwd.h
│ │ ├── Channel-inl.h
│ │ ├── Channel.h
│ │ ├── ChannelCallbackHandle.h
│ │ ├── ChannelProcessor-inl.h
│ │ ├── ChannelProcessor.h
│ │ ├── ConsumeChannel-inl.h
│ │ ├── ConsumeChannel.h
│ │ ├── FanoutChannel-inl.h
│ │ ├── FanoutChannel.h
│ │ ├── FanoutSender-inl.h
│ │ ├── FanoutSender.h
│ │ ├── MaxConcurrentRateLimiter.h
│ │ ├── Merge-inl.h
│ │ ├── Merge.h
│ │ ├── MergeChannel-inl.h
│ │ ├── MergeChannel.h
│ │ ├── MultiplexChannel-inl.h
│ │ ├── MultiplexChannel.h
│ │ ├── OnClosedException.h
│ │ ├── Producer-inl.h
│ │ ├── Producer.h
│ │ ├── ProxyChannel-inl.h
│ │ ├── ProxyChannel.h
│ │ ├── RateLimiter.h
│ │ ├── Transform-inl.h
│ │ ├── Transform.h
│ │ ├── detail/
│ │ │ ├── AtomicQueue.h
│ │ │ ├── BUCK
│ │ │ ├── CMakeLists.txt
│ │ │ ├── ChannelBridge.h
│ │ │ ├── FunctionTraits.h
│ │ │ ├── IntrusivePtr.h
│ │ │ ├── MultiplexerTraits.h
│ │ │ ├── PointerVariant.h
│ │ │ └── Utility.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── ChannelTestUtil.h
│ ├── coro/
│ │ ├── AsyncGenerator.h
│ │ ├── AsyncPipe.h
│ │ ├── AsyncScope.h
│ │ ├── AsyncStack.h
│ │ ├── AutoCleanup-fwd.h
│ │ ├── AutoCleanup.h
│ │ ├── BUCK
│ │ ├── Baton.h
│ │ ├── BlockingWait.h
│ │ ├── BoundedQueue.h
│ │ ├── CMakeLists.txt
│ │ ├── Cleanup.h
│ │ ├── Collect-inl.h
│ │ ├── Collect.h
│ │ ├── Concat-inl.h
│ │ ├── Concat.h
│ │ ├── Coroutine.h
│ │ ├── CurrentExecutor.h
│ │ ├── DetachOnCancel.h
│ │ ├── Filter-inl.h
│ │ ├── Filter.h
│ │ ├── FutureUtil.h
│ │ ├── Generator.h
│ │ ├── GmockHelpers.h
│ │ ├── GtestHelpers.h
│ │ ├── Invoke.h
│ │ ├── Merge-inl.h
│ │ ├── Merge.h
│ │ ├── Mutex.h
│ │ ├── Promise.h
│ │ ├── Result.h
│ │ ├── Retry.h
│ │ ├── RustAdaptors.h
│ │ ├── ScopeExit.h
│ │ ├── SharedLock.h
│ │ ├── SharedMutex.h
│ │ ├── SharedPromise.h
│ │ ├── Sleep-inl.h
│ │ ├── Sleep.h
│ │ ├── SmallUnboundedQueue.h
│ │ ├── Task.h
│ │ ├── TimedWait.h
│ │ ├── Timeout-inl.h
│ │ ├── Timeout.h
│ │ ├── Traits.h
│ │ ├── Transform-inl.h
│ │ ├── Transform.h
│ │ ├── UnboundedQueue.h
│ │ ├── ViaIfAsync.h
│ │ ├── WithAsyncStack.h
│ │ ├── WithCancellation.h
│ │ └── detail/
│ │ ├── Barrier.h
│ │ ├── BarrierTask.h
│ │ ├── CurrentAsyncFrame.h
│ │ ├── Helpers.h
│ │ ├── InlineTask.h
│ │ ├── Malloc.h
│ │ ├── ManualLifetime.h
│ │ └── Traits.h
│ ├── crypto/
│ │ ├── BUCK
│ │ ├── Blake2xb.h
│ │ ├── CMakeLists.txt
│ │ └── LtHash.h
│ ├── exception_tracer/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── ExceptionAbi.h
│ │ ├── ExceptionCounterLib.h
│ │ ├── ExceptionTracer.h
│ │ ├── ExceptionTracerLib.h
│ │ ├── SmartExceptionTracer.h
│ │ ├── SmartExceptionTracerSingleton.h
│ │ └── StackTrace.h
│ ├── flat_combining/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── FlatCombining.h
│ │ └── test/
│ │ ├── BUCK
│ │ ├── FlatCombiningExamples.h
│ │ └── FlatCombiningTestHelpers.h
│ ├── observer/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── CoreCachedObserver.h
│ │ ├── HazptrObserver.h
│ │ ├── Observable-inl.h
│ │ ├── Observable.h
│ │ ├── Observer-inl.h
│ │ ├── Observer-pre.h
│ │ ├── Observer.h
│ │ ├── ReadMostlyTLObserver.h
│ │ ├── SimpleObservable-inl.h
│ │ ├── SimpleObservable.h
│ │ ├── WithJitter-inl.h
│ │ ├── WithJitter.h
│ │ ├── detail/
│ │ │ ├── BUCK
│ │ │ ├── CMakeLists.txt
│ │ │ ├── Core.h
│ │ │ ├── GraphCycleDetector.h
│ │ │ └── ObserverManager.h
│ │ └── test/
│ │ └── BUCK
│ ├── settings/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Immutables.h
│ │ ├── Settings.h
│ │ ├── Types.h
│ │ └── detail/
│ │ └── SettingsImpl.h
│ ├── symbolizer/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Dwarf.h
│ │ ├── DwarfImpl.h
│ │ ├── DwarfLineNumberVM.h
│ │ ├── DwarfSection.h
│ │ ├── DwarfUtil.h
│ │ ├── Elf-inl.h
│ │ ├── Elf.h
│ │ ├── ElfCache.h
│ │ ├── LineReader.h
│ │ ├── SignalHandler.h
│ │ ├── StackTrace.h
│ │ ├── SymbolizePrinter.h
│ │ ├── SymbolizedFrame.h
│ │ ├── Symbolizer.h
│ │ └── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ └── Debug.h
│ └── test/
│ ├── BUCK
│ └── CodingTestUtils.h
├── ext/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── buck2/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ └── test_ext.cpp
│ ├── test_ext.cpp
│ └── test_ext.h
├── external/
│ ├── .clang-format
│ ├── CMakeLists.txt
│ ├── aor/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── asmdefs.h
│ │ ├── memcpy-advsimd.S
│ │ ├── memcpy-armv8.S
│ │ ├── memcpy-mops.S
│ │ ├── memcpy_sve.S
│ │ ├── memmove-mops.S
│ │ ├── memset-advsimd.S
│ │ ├── memset-mops.S
│ │ └── memset-sve.S
│ ├── farmhash/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── farmhash.cpp
│ │ ├── farmhash.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── farmhash_test.cpp
│ ├── fast-crc32/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── avx512_crc32c_v8s3x4.cpp
│ │ ├── avx512_crc32c_v8s3x4.h
│ │ ├── neon_crc32c_v3s4x2e_v2.cpp
│ │ ├── neon_crc32c_v3s4x2e_v2.h
│ │ ├── neon_eor3_crc32_v8s2x4e_s1x2.cpp
│ │ ├── neon_eor3_crc32_v8s2x4e_s1x2.h
│ │ ├── neon_eor3_crc32c_v8s2x4e_s2x1.cpp
│ │ ├── neon_eor3_crc32c_v8s2x4e_s2x1.h
│ │ ├── sse_crc32c_v8s3x3.cpp
│ │ └── sse_crc32c_v8s3x3.h
│ ├── nvidia/
│ │ ├── CMakeLists.txt
│ │ ├── detail/
│ │ │ ├── BUCK
│ │ │ ├── CMakeLists.txt
│ │ │ ├── RangeSve2.cpp
│ │ │ └── RangeSve2.h
│ │ └── hash/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Checksum.cpp
│ │ └── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Crc32cCombineDetail.h
│ │ └── Crc32cDetail.cpp
│ └── rapidhash/
│ ├── BUCK
│ ├── CMakeLists.txt
│ └── rapidhash.h
├── fibers/
│ ├── AddTasks-inl.h
│ ├── AddTasks.h
│ ├── AtomicBatchDispatcher-inl.h
│ ├── AtomicBatchDispatcher.h
│ ├── BUCK
│ ├── BatchDispatcher.h
│ ├── BatchSemaphore.cpp
│ ├── BatchSemaphore.h
│ ├── Baton-inl.h
│ ├── Baton.cpp
│ ├── Baton.h
│ ├── BoostContextCompatibility.h
│ ├── CMakeLists.txt
│ ├── CallOnce.h
│ ├── EventBaseLoopController-inl.h
│ ├── EventBaseLoopController.h
│ ├── ExecutorBasedLoopController.h
│ ├── ExecutorLoopController-inl.h
│ ├── ExecutorLoopController.h
│ ├── Fiber-inl.h
│ ├── Fiber.cpp
│ ├── Fiber.h
│ ├── FiberManager-inl.h
│ ├── FiberManager.cpp
│ ├── FiberManager.h
│ ├── FiberManagerInternal-inl.h
│ ├── FiberManagerInternal.h
│ ├── FiberManagerMap-inl.h
│ ├── FiberManagerMap.h
│ ├── ForEach-inl.h
│ ├── ForEach.h
│ ├── GenericBaton.h
│ ├── GuardPageAllocator.cpp
│ ├── GuardPageAllocator.h
│ ├── LoopController.h
│ ├── Promise-inl.h
│ ├── Promise.h
│ ├── README.md
│ ├── Semaphore.cpp
│ ├── Semaphore.h
│ ├── SemaphoreBase.cpp
│ ├── SemaphoreBase.h
│ ├── SimpleLoopController.cpp
│ ├── SimpleLoopController.h
│ ├── TimedMutex-inl.h
│ ├── TimedMutex.h
│ ├── WhenN-inl.h
│ ├── WhenN.h
│ ├── async/
│ │ ├── Async.cpp
│ │ ├── Async.h
│ │ ├── AsyncStack.h
│ │ ├── BUCK
│ │ ├── Baton.h
│ │ ├── CMakeLists.txt
│ │ ├── Collect-inl.h
│ │ ├── Collect.h
│ │ ├── FiberManager.h
│ │ ├── Future.h
│ │ ├── Promise.h
│ │ ├── README.md
│ │ ├── Task.h
│ │ ├── WaitUtils.h
│ │ └── test/
│ │ ├── AsyncTest.cpp
│ │ └── BUCK
│ ├── detail/
│ │ ├── AtomicBatchDispatcher.cpp
│ │ ├── AtomicBatchDispatcher.h
│ │ ├── BUCK
│ │ └── CMakeLists.txt
│ ├── scripts/
│ │ └── gdb.py
│ ├── test/
│ │ ├── BUCK
│ │ ├── BatchSemaphoreTest.cpp
│ │ ├── FibersBenchmark.cpp
│ │ ├── FibersTest.cpp
│ │ ├── FibersTestApp.cpp
│ │ ├── SemaphoreTest.cpp
│ │ ├── StackOverflow.cpp
│ │ └── TimedMutexBenchmark.cpp
│ └── traits.h
├── folly_extended_library.bzl
├── functional/
│ ├── ApplyTuple.h
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Invoke.h
│ ├── Partial.h
│ ├── protocol.h
│ ├── test/
│ │ ├── ApplyTupleTest.cpp
│ │ ├── BUCK
│ │ ├── InvokeTest.cpp
│ │ ├── PartialTest.cpp
│ │ ├── protocol_test.cpp
│ │ └── traits_test.cpp
│ └── traits.h
├── futures/
│ ├── BUCK
│ ├── Barrier.cpp
│ ├── Barrier.h
│ ├── CMakeLists.txt
│ ├── Cleanup.h
│ ├── Future-inl.h
│ ├── Future-pre.h
│ ├── Future.cpp
│ ├── Future.h
│ ├── FutureSplitter.h
│ ├── HeapTimekeeper.cpp
│ ├── HeapTimekeeper.h
│ ├── ManualTimekeeper.cpp
│ ├── ManualTimekeeper.h
│ ├── Portability.h
│ ├── Promise-inl.h
│ ├── Promise.cpp
│ ├── Promise.h
│ ├── Retrying.h
│ ├── SharedPromise-inl.h
│ ├── SharedPromise.cpp
│ ├── SharedPromise.h
│ ├── ThreadWheelTimekeeper.cpp
│ ├── ThreadWheelTimekeeper.h
│ ├── WTCallback.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Core.cpp
│ │ ├── Core.h
│ │ └── Types.h
│ └── test/
│ ├── BUCK
│ ├── BarrierTest.cpp
│ ├── Benchmark.cpp
│ ├── CallbackLifetimeTest.cpp
│ ├── CleanupTest.cpp
│ ├── CollectTest.cpp
│ ├── ContextTest.cpp
│ ├── ConversionOperatorTest.cpp
│ ├── CoreTest.cpp
│ ├── EnsureTest.cpp
│ ├── FilterTest.cpp
│ ├── FutureSplitterTest.cpp
│ ├── FutureTest.cpp
│ ├── HeaderCompileTest.cpp
│ ├── HeapTimekeeperTest.cpp
│ ├── InterruptTest.cpp
│ ├── ManualTimekeeperTest.cpp
│ ├── MapTest.cpp
│ ├── NonCopyableLambdaTest.cpp
│ ├── PollTest.cpp
│ ├── PromiseTest.cpp
│ ├── ReduceTest.cpp
│ ├── RetryingTest.cpp
│ ├── SelfDestructTest.cpp
│ ├── SemiFutureTest.cpp
│ ├── SharedPromiseTest.cpp
│ ├── TestExecutor.cpp
│ ├── TestExecutor.h
│ ├── TestExecutorTest.cpp
│ ├── ThenCompileTest.cpp
│ ├── ThenCompileTest.h
│ ├── ThenTest.cpp
│ ├── ThreadWheelTimekeeperTest.cpp
│ ├── TimekeeperTest.cpp
│ ├── TimekeeperTestLib.h
│ ├── TimesTest.cpp
│ ├── UnwrapTest.cpp
│ ├── ViaTest.cpp
│ ├── WaitTest.cpp
│ ├── WhenTest.cpp
│ ├── WhileDoTest.cpp
│ ├── WillEqualTest.cpp
│ ├── WindowTest.cpp
│ └── then_compile_test.rb
├── gen/
│ ├── BUCK
│ ├── Base-inl.h
│ ├── Base.h
│ ├── CMakeLists.txt
│ ├── Combine-inl.h
│ ├── Combine.h
│ ├── Core-inl.h
│ ├── Core.h
│ ├── File-inl.h
│ ├── File.h
│ ├── IStream.h
│ ├── Parallel-inl.h
│ ├── Parallel.h
│ ├── ParallelMap-inl.h
│ ├── ParallelMap.h
│ ├── String-inl.h
│ ├── String.h
│ └── test/
│ ├── BUCK
│ ├── BaseBenchmark.cpp
│ ├── BaseTest.cpp
│ ├── Bench.h
│ ├── CombineTest.cpp
│ ├── FileBenchmark.cpp
│ ├── FileTest.cpp
│ ├── IStreamTest.cpp
│ ├── ParallelBenchmark.cpp
│ ├── ParallelMapBenchmark.cpp
│ ├── ParallelMapTest.cpp
│ ├── ParallelTest.cpp
│ ├── StringBenchmark.cpp
│ └── StringTest.cpp
├── hash/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Checksum.cpp
│ ├── Checksum.h
│ ├── FarmHash.h
│ ├── FnvHash.h
│ ├── Hash.h
│ ├── HsiehHash.h
│ ├── MurmurHash.h
│ ├── SpookyHashV1.cpp
│ ├── SpookyHashV1.h
│ ├── SpookyHashV2.cpp
│ ├── SpookyHashV2.h
│ ├── UniqueHashKey.cpp
│ ├── UniqueHashKey.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── ChecksumDetail.cpp
│ │ ├── ChecksumDetail.h
│ │ ├── Crc32CombineDetail.cpp
│ │ ├── Crc32cDetail.cpp
│ │ ├── RandomSeed.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── RandomSeedTest.cpp
│ ├── rapidhash.h
│ ├── test/
│ │ ├── BUCK
│ │ ├── ChecksumBenchmark.cpp
│ │ ├── ChecksumTest.cpp
│ │ ├── FarmHashTest.cpp
│ │ ├── FnvHashTest.cpp
│ │ ├── HashBenchmark.cpp
│ │ ├── HashTest.cpp
│ │ ├── HsiehHashTest.cpp
│ │ ├── MurmurHashTest.cpp
│ │ ├── RapidHashTest.cpp
│ │ ├── SpookyHashV1Test.cpp
│ │ ├── SpookyHashV2Test.cpp
│ │ ├── StdCompatibleHashTest.cpp
│ │ ├── UniqueHashKeyTest.cpp
│ │ └── traits_test.cpp
│ └── traits.h
├── init/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Init.cpp
│ ├── Init.h
│ ├── Phase.cpp
│ ├── Phase.h
│ └── test/
│ ├── BUCK
│ └── PhaseTest.cpp
├── io/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Cursor-inl.h
│ ├── Cursor.cpp
│ ├── Cursor.h
│ ├── FsUtil.cpp
│ ├── FsUtil.h
│ ├── GlobalShutdownSocketSet.cpp
│ ├── GlobalShutdownSocketSet.h
│ ├── HugePages.cpp
│ ├── HugePages.h
│ ├── IOBuf.cpp
│ ├── IOBuf.h
│ ├── IOBufIovecBuilder.cpp
│ ├── IOBufIovecBuilder.h
│ ├── IOBufQueue.cpp
│ ├── IOBufQueue.h
│ ├── RecordIO-inl.h
│ ├── RecordIO.cpp
│ ├── RecordIO.h
│ ├── ShutdownSocketSet.cpp
│ ├── ShutdownSocketSet.h
│ ├── SocketOptionMap.cpp
│ ├── SocketOptionMap.h
│ ├── SocketOptionValue.cpp
│ ├── SocketOptionValue.h
│ ├── TypedIOBuf.h
│ ├── async/
│ │ ├── AsyncBase.cpp
│ │ ├── AsyncBase.h
│ │ ├── AsyncIO.cpp
│ │ ├── AsyncIO.h
│ │ ├── AsyncIoUringSocket.cpp
│ │ ├── AsyncIoUringSocket.h
│ │ ├── AsyncIoUringSocketFactory.cpp
│ │ ├── AsyncIoUringSocketFactory.h
│ │ ├── AsyncPipe.cpp
│ │ ├── AsyncPipe.h
│ │ ├── AsyncSSLSocket.cpp
│ │ ├── AsyncSSLSocket.h
│ │ ├── AsyncServerSocket.cpp
│ │ ├── AsyncServerSocket.h
│ │ ├── AsyncSignalHandler.cpp
│ │ ├── AsyncSignalHandler.h
│ │ ├── AsyncSocket.cpp
│ │ ├── AsyncSocket.h
│ │ ├── AsyncSocketBase.h
│ │ ├── AsyncSocketException.cpp
│ │ ├── AsyncSocketException.h
│ │ ├── AsyncSocketTransport.cpp
│ │ ├── AsyncSocketTransport.h
│ │ ├── AsyncTimeout.cpp
│ │ ├── AsyncTimeout.h
│ │ ├── AsyncTransport.h
│ │ ├── AsyncTransportCertificate.h
│ │ ├── AsyncUDPServerSocket.h
│ │ ├── AsyncUDPSocket.cpp
│ │ ├── AsyncUDPSocket.h
│ │ ├── AtomicNotificationQueue-inl.h
│ │ ├── AtomicNotificationQueue.h
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── CertificateIdentityVerifier.h
│ │ ├── DecoratedAsyncTransportWrapper.h
│ │ ├── DelayedDestruction.cpp
│ │ ├── DelayedDestruction.h
│ │ ├── DelayedDestructionBase.h
│ │ ├── DestructorCheck.h
│ │ ├── Epoll.h
│ │ ├── EpollBackend.cpp
│ │ ├── EpollBackend.h
│ │ ├── EventBase.cpp
│ │ ├── EventBase.h
│ │ ├── EventBaseAtomicNotificationQueue-inl.h
│ │ ├── EventBaseAtomicNotificationQueue.h
│ │ ├── EventBaseBackendBase.cpp
│ │ ├── EventBaseBackendBase.h
│ │ ├── EventBaseLocal.cpp
│ │ ├── EventBaseLocal.h
│ │ ├── EventBaseManager.cpp
│ │ ├── EventBaseManager.h
│ │ ├── EventBasePoller.cpp
│ │ ├── EventBasePoller.h
│ │ ├── EventBaseThread.cpp
│ │ ├── EventBaseThread.h
│ │ ├── EventHandler.cpp
│ │ ├── EventHandler.h
│ │ ├── EventUtil.h
│ │ ├── HHWheelTimer-fwd.h
│ │ ├── HHWheelTimer.cpp
│ │ ├── HHWheelTimer.h
│ │ ├── IoUring.cpp
│ │ ├── IoUring.h
│ │ ├── IoUringBackend.cpp
│ │ ├── IoUringBackend.h
│ │ ├── IoUringBase.h
│ │ ├── IoUringBufferPoolSharing.cpp
│ │ ├── IoUringBufferPoolSharing.h
│ │ ├── IoUringConnect.cpp
│ │ ├── IoUringConnect.h
│ │ ├── IoUringEvent.cpp
│ │ ├── IoUringEvent.h
│ │ ├── IoUringEventBaseLocal.cpp
│ │ ├── IoUringEventBaseLocal.h
│ │ ├── IoUringOptions.h
│ │ ├── IoUringProvidedBufferRing.cpp
│ │ ├── IoUringProvidedBufferRing.h
│ │ ├── IoUringRecv.cpp
│ │ ├── IoUringRecv.h
│ │ ├── IoUringSend.cpp
│ │ ├── IoUringSend.h
│ │ ├── IoUringZeroCopyBufferPool.cpp
│ │ ├── IoUringZeroCopyBufferPool.h
│ │ ├── Liburing.h
│ │ ├── MuxIOThreadPoolExecutor.cpp
│ │ ├── MuxIOThreadPoolExecutor.h
│ │ ├── NotificationQueue.h
│ │ ├── PasswordInFile.cpp
│ │ ├── PasswordInFile.h
│ │ ├── README.md
│ │ ├── Request.cpp
│ │ ├── Request.h
│ │ ├── SSLContext.cpp
│ │ ├── SSLContext.h
│ │ ├── SSLOptions.cpp
│ │ ├── SSLOptions.h
│ │ ├── STTimerFDTimeoutManager.cpp
│ │ ├── STTimerFDTimeoutManager.h
│ │ ├── ScopedEventBaseThread.cpp
│ │ ├── ScopedEventBaseThread.h
│ │ ├── SimpleAsyncIO.cpp
│ │ ├── SimpleAsyncIO.h
│ │ ├── TerminateCancellationToken.cpp
│ │ ├── TerminateCancellationToken.h
│ │ ├── TimeoutManager.cpp
│ │ ├── TimeoutManager.h
│ │ ├── TimerFD.cpp
│ │ ├── TimerFD.h
│ │ ├── TimerFDTimeoutManager.cpp
│ │ ├── TimerFDTimeoutManager.h
│ │ ├── VirtualEventBase.cpp
│ │ ├── VirtualEventBase.h
│ │ ├── WriteChainAsyncTransportWrapper.h
│ │ ├── WriteFlags.h
│ │ ├── fdsock/
│ │ │ ├── AsyncFdSocket.cpp
│ │ │ ├── AsyncFdSocket.h
│ │ │ ├── BUCK
│ │ │ ├── CMakeLists.txt
│ │ │ ├── SocketFds.cpp
│ │ │ ├── SocketFds.h
│ │ │ └── test/
│ │ │ ├── AsyncFdSocketTest.cpp
│ │ │ └── BUCK
│ │ ├── observer/
│ │ │ ├── AsyncSocketObserverContainer.h
│ │ │ ├── AsyncSocketObserverInterface.h
│ │ │ ├── BUCK
│ │ │ └── CMakeLists.txt
│ │ ├── ssl/
│ │ │ ├── BUCK
│ │ │ ├── BasicTransportCertificate.h
│ │ │ ├── CMakeLists.txt
│ │ │ ├── OpenSSLTransportCertificate.h
│ │ │ ├── OpenSSLUtils.cpp
│ │ │ ├── OpenSSLUtils.h
│ │ │ ├── SSLErrors.cpp
│ │ │ ├── SSLErrors.h
│ │ │ ├── TLSDefinitions.h
│ │ │ └── test/
│ │ │ ├── BUCK
│ │ │ ├── BasicTransportCertificateTest.cpp
│ │ │ ├── OpenSSLUtilsTest.cpp
│ │ │ └── SSLErrorsTest.cpp
│ │ └── test/
│ │ ├── AsyncBaseTestLib.cpp
│ │ ├── AsyncBaseTestLib.h
│ │ ├── AsyncIOTest.cpp
│ │ ├── AsyncIoUringSocketTest.cpp
│ │ ├── AsyncPipeTest.cpp
│ │ ├── AsyncSSLSocketTest.cpp
│ │ ├── AsyncSSLSocketTest.h
│ │ ├── AsyncSSLSocketTest2.cpp
│ │ ├── AsyncSSLSocketWriteTest.cpp
│ │ ├── AsyncSignalHandlerTestLib.h
│ │ ├── AsyncSocketExceptionTest.cpp
│ │ ├── AsyncSocketObserverTest.cpp
│ │ ├── AsyncSocketTest.cpp
│ │ ├── AsyncSocketTest.h
│ │ ├── AsyncSocketTest2.cpp
│ │ ├── AsyncSocketTest2.h
│ │ ├── AsyncTimeoutTest.cpp
│ │ ├── AsyncTransportTest.cpp
│ │ ├── AsyncUDPSocketGSOGROTest.cpp
│ │ ├── AsyncUDPSocketSendmmsgTest.cpp
│ │ ├── AsyncUDPSocketTest.cpp
│ │ ├── AtomicNotificationQueueTest.cpp
│ │ ├── BUCK
│ │ ├── BlockingSocket.h
│ │ ├── CallbackStateEnum.h
│ │ ├── ConnCallback.h
│ │ ├── DecoratedAsyncTransportWrapperTest.cpp
│ │ ├── DelayedDestructionBaseTest.cpp
│ │ ├── DelayedDestructionTest.cpp
│ │ ├── DestructorCheckTest.cpp
│ │ ├── EpollBackendTest.cpp
│ │ ├── EventBaseBenchmark.cpp
│ │ ├── EventBaseLocalTest.cpp
│ │ ├── EventBaseTest.cpp
│ │ ├── EventBaseTestLib.h
│ │ ├── EventBaseThreadTest.cpp
│ │ ├── EventHandlerTest.cpp
│ │ ├── HHWheelTimerHighResBenchmark.cpp
│ │ ├── HHWheelTimerHighResTest.cpp
│ │ ├── HHWheelTimerSlowTests.cpp
│ │ ├── HHWheelTimerTest.cpp
│ │ ├── IOBenchmark.cpp
│ │ ├── IoTestTempFileUtil.cpp
│ │ ├── IoTestTempFileUtil.h
│ │ ├── IoUringBackendBench.cpp
│ │ ├── IoUringBackendSetupTest.cpp
│ │ ├── IoUringBackendTest.cpp
│ │ ├── IoUringEventBaseLocalTest.cpp
│ │ ├── IoUringEventTest.cpp
│ │ ├── IoUringProvidedBufferRingTest.cpp
│ │ ├── IoUringTest.cpp
│ │ ├── IoUringZeroCopyBufferPoolTest.cpp
│ │ ├── MockAsyncSSLSocket.h
│ │ ├── MockAsyncServerSocket.h
│ │ ├── MockAsyncSocket.h
│ │ ├── MockAsyncSocketLegacyObserver.h
│ │ ├── MockAsyncSocketObserver.h
│ │ ├── MockAsyncTransport.h
│ │ ├── MockAsyncUDPSocket.h
│ │ ├── MockTimeoutManager.h
│ │ ├── MuxIOThreadPoolExecutorTest.cpp
│ │ ├── NotificationQueueBenchmark.cpp
│ │ ├── NotificationQueueTest.cpp
│ │ ├── PollLoopHooksTest.cpp
│ │ ├── RegisteredFdBenchmark.cpp
│ │ ├── RequestContextBenchmark.cpp
│ │ ├── RequestContextHelper.h
│ │ ├── RequestContextTest.cpp
│ │ ├── SSLContextRegressionTest.cpp
│ │ ├── SSLContextTest.cpp
│ │ ├── SSLOptionsTest.cpp
│ │ ├── SSLSessionTest.cpp
│ │ ├── SSLUtil.cpp
│ │ ├── SSLUtil.h
│ │ ├── ScopedBoundPort.cpp
│ │ ├── ScopedBoundPort.h
│ │ ├── ScopedEventBaseThreadTest.cpp
│ │ ├── SimpleAsyncIOTest.cpp
│ │ ├── SocketClient.cpp
│ │ ├── SocketPair.cpp
│ │ ├── SocketPair.h
│ │ ├── TFOUtil.cpp
│ │ ├── TFOUtil.h
│ │ ├── TerminateCancellationTokenTest.cpp
│ │ ├── TestSSLServer.cpp
│ │ ├── TestSSLServer.h
│ │ ├── TimeUtil.cpp
│ │ ├── TimeUtil.h
│ │ ├── TimeUtilTest.cpp
│ │ ├── TimerFDTimeoutManagerTest.cpp
│ │ ├── UndelayedDestruction.h
│ │ ├── Util.h
│ │ ├── WriteChainAsyncTransportWrapperTest.cpp
│ │ ├── WriteFlagsTest.cpp
│ │ ├── ZeroCopy.cpp
│ │ ├── ZeroCopy.h
│ │ ├── ZeroCopyBenchmark.cpp
│ │ ├── ZeroCopyTest.cpp
│ │ └── certs/
│ │ ├── BUCK
│ │ ├── ca-cert.pem
│ │ ├── ca-key.pem
│ │ ├── client_ca_cert.pem
│ │ ├── client_ca_key.pem
│ │ ├── client_cert.pem
│ │ ├── client_chain.pem
│ │ ├── client_intermediate_ca.pem
│ │ ├── client_intermediate_ca_key.pem
│ │ ├── client_key.pem
│ │ ├── clienti_cert.pem
│ │ ├── clienti_key.pem
│ │ ├── defs.bzl
│ │ ├── generate_certs.sh
│ │ ├── tests-cert.pem
│ │ └── tests-key.pem
│ ├── coro/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── ServerSocket.cpp
│ │ ├── ServerSocket.h
│ │ ├── Transport.cpp
│ │ ├── Transport.h
│ │ ├── TransportCallbackBase.h
│ │ ├── TransportCallbacks.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── TransportTest.cpp
│ ├── test/
│ │ ├── BUCK
│ │ ├── FsUtilTest.cpp
│ │ ├── IOBufBenchmark.cpp
│ │ ├── IOBufCBTest.cpp
│ │ ├── IOBufCursorBenchmark.cpp
│ │ ├── IOBufCursorTest.cpp
│ │ ├── IOBufIovecBuilderTest.cpp
│ │ ├── IOBufQueueTest.cpp
│ │ ├── IOBufTest.cpp
│ │ ├── NetworkBenchmark.cpp
│ │ ├── QueueAppenderBenchmark.cpp
│ │ ├── RecordIOTest.cpp
│ │ ├── ShutdownSocketSetTest.cpp
│ │ └── SocketOptionValueTest.cpp
│ └── tool/
│ ├── BUCK
│ └── HugePageUtil.cpp
├── json/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── DynamicConverter.h
│ ├── DynamicParser-inl.h
│ ├── DynamicParser.cpp
│ ├── DynamicParser.h
│ ├── JSONSchema.cpp
│ ├── JSONSchema.h
│ ├── JsonMockUtil.h
│ ├── JsonTestUtil.cpp
│ ├── JsonTestUtil.h
│ ├── bser/
│ │ ├── BUCK
│ │ ├── Bser.h
│ │ ├── CMakeLists.txt
│ │ ├── Dump.cpp
│ │ ├── Load.cpp
│ │ └── test/
│ │ ├── BUCK
│ │ └── BserTest.cpp
│ ├── dynamic-inl.h
│ ├── dynamic.cpp
│ ├── dynamic.h
│ ├── json.cpp
│ ├── json.h
│ ├── json_patch.cpp
│ ├── json_patch.h
│ ├── json_pointer.cpp
│ ├── json_pointer.h
│ ├── test/
│ │ ├── BUCK
│ │ ├── DynamicBenchmark.cpp
│ │ ├── DynamicConverterTest.cpp
│ │ ├── DynamicOtherTest.cpp
│ │ ├── DynamicParserTest.cpp
│ │ ├── DynamicTest.cpp
│ │ ├── JSONSchemaTest.cpp
│ │ ├── Json5Test.cpp
│ │ ├── JsonBenchmark.cpp
│ │ ├── JsonOtherTest.cpp
│ │ ├── JsonTest.cpp
│ │ ├── JsonTestUtilTest.cpp
│ │ ├── json_patch_test.cpp
│ │ └── json_pointer_test.cpp
│ └── tool/
│ ├── BUCK
│ └── JSONSchemaTester.cpp
├── json.h
├── json_patch.h
├── json_pointer.h
├── lang/
│ ├── Access.h
│ ├── Align.h
│ ├── Aligned.h
│ ├── Assume.h
│ ├── BUCK
│ ├── Badge.h
│ ├── Bits.h
│ ├── BitsClass.h
│ ├── Builtin.h
│ ├── CArray.h
│ ├── CMakeLists.txt
│ ├── CString.cpp
│ ├── CString.h
│ ├── Cast.h
│ ├── CheckedMath.h
│ ├── CustomizationPoint.h
│ ├── Exception.cpp
│ ├── Exception.h
│ ├── Extern.h
│ ├── Hint-inl.h
│ ├── Hint.h
│ ├── Keep.h
│ ├── MustUseImmediately.h
│ ├── New.h
│ ├── Ordering.h
│ ├── Pretty.h
│ ├── PropagateConst.h
│ ├── RValueReferenceWrapper.h
│ ├── SafeAlias-fwd.h
│ ├── SafeAlias.h
│ ├── SafeAssert.cpp
│ ├── SafeAssert.h
│ ├── SafeClosure.h
│ ├── SafeClosure.md
│ ├── StaticConst.h
│ ├── Strong.h
│ ├── Switch.h
│ ├── Thunk.h
│ ├── ToAscii.cpp
│ ├── ToAscii.h
│ ├── TypeInfo.h
│ ├── UncaughtExceptions.cpp
│ ├── UncaughtExceptions.h
│ ├── VectorTraits.h
│ ├── bind/
│ │ ├── AsArgument.h
│ │ ├── BUCK
│ │ ├── Bind.h
│ │ ├── Bind.md
│ │ ├── CMakeLists.txt
│ │ ├── Named.h
│ │ ├── NamedToStorage.h
│ │ ├── ToStorage.h
│ │ └── test/
│ │ ├── AsArgumentTest.cpp
│ │ ├── BUCK
│ │ ├── BindTest.cpp
│ │ ├── NamedTest.cpp
│ │ ├── NamedToStorageTest.cpp
│ │ └── ToStorageTest.cpp
│ ├── cstring_view.h
│ └── test/
│ ├── AlignTest.cpp
│ ├── AlignedTest.cpp
│ ├── BUCK
│ ├── BadgeTest.cpp
│ ├── BitsBenchmark.cpp
│ ├── BitsClassBenchmark.cpp
│ ├── BitsClassTest.cpp
│ ├── BitsTest.cpp
│ ├── CStringTest.cpp
│ ├── CastTest.cpp
│ ├── CheckedMathTest.cpp
│ ├── ExceptionBench.cpp
│ ├── ExceptionTest.cpp
│ ├── ExternTest.cpp
│ ├── HintTest.cpp
│ ├── MustUseImmediatelyTest.cpp
│ ├── NewTest.cpp
│ ├── OrderingTest.cpp
│ ├── PrettyTest.cpp
│ ├── PropagateConstTest.cpp
│ ├── RValueReferenceWrapperTest.cpp
│ ├── SafeAliasTest.cpp
│ ├── SafeAssertTest.cpp
│ ├── SafeClosureTest.cpp
│ ├── StrongTest.cpp
│ ├── SwitchTest.cpp
│ ├── ThunkTest.cpp
│ ├── ToAsciiBench.cpp
│ ├── ToAsciiTest.cpp
│ ├── TypeInfoTest.cpp
│ ├── VectorTraitsTest.cpp
│ └── cstring_view_test.cpp
├── logging/
│ ├── AsyncFileWriter.cpp
│ ├── AsyncFileWriter.h
│ ├── AsyncLogWriter.cpp
│ ├── AsyncLogWriter.h
│ ├── AutoTimer.h
│ ├── BUCK
│ ├── BridgeFromGoogleLogging.cpp
│ ├── BridgeFromGoogleLogging.h
│ ├── CMakeLists.txt
│ ├── CustomLogFormatter.cpp
│ ├── CustomLogFormatter.h
│ ├── FileHandlerFactory.cpp
│ ├── FileHandlerFactory.h
│ ├── FileWriterFactory.cpp
│ ├── FileWriterFactory.h
│ ├── GlogStyleFormatter.cpp
│ ├── GlogStyleFormatter.h
│ ├── ImmediateFileWriter.cpp
│ ├── ImmediateFileWriter.h
│ ├── Init.cpp
│ ├── Init.h
│ ├── InitWeak.cpp
│ ├── LogCategory.cpp
│ ├── LogCategory.h
│ ├── LogCategoryConfig.cpp
│ ├── LogCategoryConfig.h
│ ├── LogConfig.cpp
│ ├── LogConfig.h
│ ├── LogConfigParser.cpp
│ ├── LogConfigParser.h
│ ├── LogFormatter.h
│ ├── LogHandler.h
│ ├── LogHandlerConfig.cpp
│ ├── LogHandlerConfig.h
│ ├── LogHandlerFactory.h
│ ├── LogLevel.cpp
│ ├── LogLevel.h
│ ├── LogMessage.cpp
│ ├── LogMessage.h
│ ├── LogName.cpp
│ ├── LogName.h
│ ├── LogStream.cpp
│ ├── LogStream.h
│ ├── LogStreamProcessor.cpp
│ ├── LogStreamProcessor.h
│ ├── LogWriter.h
│ ├── Logger.cpp
│ ├── Logger.h
│ ├── LoggerDB.cpp
│ ├── LoggerDB.h
│ ├── ObjectToString.cpp
│ ├── ObjectToString.h
│ ├── README.md
│ ├── RateLimiter.cpp
│ ├── RateLimiter.h
│ ├── StandardLogHandler.cpp
│ ├── StandardLogHandler.h
│ ├── StandardLogHandlerFactory.cpp
│ ├── StandardLogHandlerFactory.h
│ ├── StreamHandlerFactory.cpp
│ ├── StreamHandlerFactory.h
│ ├── docs/
│ │ ├── Comparisons.md
│ │ ├── Config.md
│ │ ├── LogCategories.md
│ │ ├── LogHandlers.md
│ │ ├── LogLevels.md
│ │ ├── Overview.md
│ │ └── Usage.md
│ ├── example/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── lib.cpp
│ │ ├── lib.h
│ │ └── main.cpp
│ ├── test/
│ │ ├── AsyncFileWriterTest.cpp
│ │ ├── AsyncLogWriterTest.cpp
│ │ ├── AutoTimerTest.cpp
│ │ ├── BUCK
│ │ ├── BridgeFromGoogleLoggingTest.cpp
│ │ ├── ConfigHelpers.cpp
│ │ ├── ConfigHelpers.h
│ │ ├── ConfigParserTest.cpp
│ │ ├── ConfigUpdateTest.cpp
│ │ ├── CustomLogFormatterTest.cpp
│ │ ├── FileHandlerFactoryTest.cpp
│ │ ├── GlogFormatterTest.cpp
│ │ ├── ImmediateFileWriterTest.cpp
│ │ ├── InitTest.cpp
│ │ ├── LogCategoryTest.cpp
│ │ ├── LogLevelTest.cpp
│ │ ├── LogMessageTest.cpp
│ │ ├── LogNameTest.cpp
│ │ ├── LogStreamTest.cpp
│ │ ├── LoggerDBTest.cpp
│ │ ├── LoggerTest.cpp
│ │ ├── RateLimiterTest.cpp
│ │ ├── StandardLogHandlerFactoryTest.cpp
│ │ ├── StandardLogHandlerTest.cpp
│ │ ├── SyncLevelTest.cpp
│ │ ├── TestLogHandler.cpp
│ │ ├── TestLogHandler.h
│ │ ├── XlogBench.cpp
│ │ ├── XlogFile1.cpp
│ │ ├── XlogFile2.cpp
│ │ ├── XlogHeader1.h
│ │ ├── XlogHeader2.h
│ │ ├── XlogTest.cpp
│ │ ├── fatal_test.py
│ │ ├── helpers/
│ │ │ ├── BUCK
│ │ │ ├── FatalHelper.cpp
│ │ │ ├── LogAfterMain.cpp
│ │ │ ├── LogAfterMainNoInit.cpp
│ │ │ ├── LogOnShutdownLib.cpp
│ │ │ ├── LogOnShutdownLib.h
│ │ │ └── helpers.h
│ │ └── log_after_main.py
│ ├── xlog.cpp
│ └── xlog.h
├── memcpy.S
├── memcpy_select_aarch64.cpp
├── memory/
│ ├── Arena-inl.h
│ ├── Arena.h
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── IOBufArenaFactory.h
│ ├── IoUringArena.cpp
│ ├── IoUringArena.h
│ ├── IoUringArenaTest.cpp
│ ├── JemallocHugePageAllocator.cpp
│ ├── JemallocHugePageAllocator.h
│ ├── JemallocHugePageAllocatorTest.cpp
│ ├── JemallocNodumpAllocator.cpp
│ ├── JemallocNodumpAllocator.h
│ ├── JemallocNodumpAllocatorTest.cpp
│ ├── MallctlHelper.cpp
│ ├── MallctlHelper.h
│ ├── Malloc.cpp
│ ├── Malloc.h
│ ├── MemoryResource.h
│ ├── ReentrantAllocator.cpp
│ ├── ReentrantAllocator.h
│ ├── SanitizeAddress.cpp
│ ├── SanitizeAddress.h
│ ├── SanitizeLeak.cpp
│ ├── SanitizeLeak.h
│ ├── ThreadCachedArena.cpp
│ ├── ThreadCachedArena.h
│ ├── UninitializedMemoryHacks.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── MallocImpl.cpp
│ │ └── MallocImpl.h
│ ├── not_null-inl.h
│ ├── not_null.h
│ ├── shared_from_this_ptr.h
│ └── test/
│ ├── ArenaTest.cpp
│ ├── BUCK
│ ├── IOBufArenaFactoryTest.cpp
│ ├── MallctlHelperTest.cpp
│ ├── MallocBenchmark.cpp
│ ├── MallocTest.cpp
│ ├── MemoryResourceTest.cpp
│ ├── ReentrantAllocatorTest.cpp
│ ├── SanitizeAddressTest.cpp
│ ├── SanitizeLeakTest.cpp
│ ├── ThreadCachedArenaTest.cpp
│ ├── UninitializedMemoryHacksODR.cpp
│ ├── UninitializedMemoryHacksTest.cpp
│ ├── not_null_test.cpp
│ └── shared_from_this_ptr_test.cpp
├── memset.S
├── memset_select_aarch64.cpp
├── net/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── NetOps.cpp
│ ├── NetOps.h
│ ├── NetOpsDispatcher.cpp
│ ├── NetOpsDispatcher.h
│ ├── NetworkSocket.h
│ ├── TcpInfo.cpp
│ ├── TcpInfo.h
│ ├── TcpInfoDispatcher.cpp
│ ├── TcpInfoDispatcher.h
│ ├── TcpInfoTypes.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── SocketFileDescriptorMap.cpp
│ │ ├── SocketFileDescriptorMap.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── SocketFileDescriptorMapTest.cpp
│ └── test/
│ ├── BUCK
│ ├── MockNetOpsDispatcher.h
│ ├── MockTcpInfoDispatcher.h
│ ├── NetOpsTest.cpp
│ ├── TcpInfoTest.cpp
│ └── TcpInfoTestUtil.h
├── observer/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── CoreCachedObserver.h
│ ├── HazptrObserver.h
│ ├── Observable-inl.h
│ ├── Observable.h
│ ├── Observer-inl.h
│ ├── Observer-pre.h
│ ├── Observer.h
│ ├── ReadMostlyTLObserver.h
│ ├── SimpleObservable-inl.h
│ ├── SimpleObservable.h
│ ├── WithJitter-inl.h
│ ├── WithJitter.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── Core.cpp
│ │ ├── Core.h
│ │ ├── GraphCycleDetector.h
│ │ ├── ObserverManager.cpp
│ │ └── ObserverManager.h
│ └── test/
│ ├── BUCK
│ ├── ObserverBenchmark.cpp
│ └── ObserverTest.cpp
├── poly/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── Nullable.h
│ └── Regular.h
├── portability/
│ ├── Asm.h
│ ├── Atomic.h
│ ├── BUCK
│ ├── Builtins.cpp
│ ├── Builtins.h
│ ├── CMakeLists.txt
│ ├── Config.h
│ ├── Constexpr.h
│ ├── Dirent.cpp
│ ├── Dirent.h
│ ├── Event.h
│ ├── Fcntl.cpp
│ ├── Fcntl.h
│ ├── Filesystem.cpp
│ ├── Filesystem.h
│ ├── FmtCompile.h
│ ├── GFlags.cpp
│ ├── GFlags.h
│ ├── GMock.h
│ ├── GTest.h
│ ├── GTestProd.h
│ ├── IOVec.h
│ ├── Libgen.cpp
│ ├── Libgen.h
│ ├── Libunwind.h
│ ├── Malloc.cpp
│ ├── Malloc.h
│ ├── Math.h
│ ├── Memory.h
│ ├── OpenSSL.cpp
│ ├── OpenSSL.h
│ ├── PThread.cpp
│ ├── PThread.h
│ ├── README.md
│ ├── Sched.cpp
│ ├── Sched.h
│ ├── Sockets.cpp
│ ├── Sockets.h
│ ├── SourceLocation.h
│ ├── Stdio.cpp
│ ├── Stdio.h
│ ├── Stdlib.cpp
│ ├── Stdlib.h
│ ├── String.cpp
│ ├── String.h
│ ├── SysFile.cpp
│ ├── SysFile.h
│ ├── SysMembarrier.cpp
│ ├── SysMembarrier.h
│ ├── SysMman.cpp
│ ├── SysMman.h
│ ├── SysResource.cpp
│ ├── SysResource.h
│ ├── SysStat.cpp
│ ├── SysStat.h
│ ├── SysSyscall.h
│ ├── SysTime.cpp
│ ├── SysTime.h
│ ├── SysTypes.h
│ ├── SysUio.cpp
│ ├── SysUio.h
│ ├── Syslog.h
│ ├── Time.cpp
│ ├── Time.h
│ ├── Unistd.cpp
│ ├── Unistd.h
│ ├── Windows.h
│ ├── provide/
│ │ ├── BUCK
│ │ └── CMakeLists.txt
│ └── test/
│ ├── BUCK
│ ├── ConstexprTest.cpp
│ ├── FcntlTest.cpp
│ ├── FilesystemTest.cpp
│ ├── LibgenTest.cpp
│ ├── OpenSSLPortabilityTest.cpp
│ ├── PThreadTest.cpp
│ ├── TimeTest.cpp
│ └── UnistdTest.cpp
├── python/
│ ├── AsyncioExecutor.cpp
│ ├── AsyncioExecutor.h
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── GILAwareManualExecutor.cpp
│ ├── GILAwareManualExecutor.h
│ ├── ProactorExecutor.cpp
│ ├── ProactorExecutor.h
│ ├── ScopedGILRelease.h
│ ├── Weak.h
│ ├── __init__.pxd
│ ├── async_generator.h
│ ├── async_generator.pxd
│ ├── build_mode.pyi
│ ├── build_mode.pyx
│ ├── cast.pxd
│ ├── coro.h
│ ├── coro.pxd
│ ├── error.cpp
│ ├── error.h
│ ├── executor.cpp
│ ├── executor.h
│ ├── executor.pxd
│ ├── executor.pyx
│ ├── executor_detail.pxd
│ ├── expected.pxd
│ ├── fbstring.pxd
│ ├── fiber_manager.pxd
│ ├── fiber_manager.pyx
│ ├── fibers.cpp
│ ├── fibers.h
│ ├── fibers.pxd
│ ├── function.pxd
│ ├── futures.h
│ ├── futures.pxd
│ ├── import.h
│ ├── iobuf.cpp
│ ├── iobuf.h
│ ├── iobuf.pxd
│ ├── iobuf.pyi
│ ├── iobuf.pyx
│ ├── iobuf_ext.cpp
│ ├── iobuf_ext.h
│ ├── memory.pxd
│ ├── optional.pxd
│ ├── range.pxd
│ ├── request_context.h
│ ├── request_context.pxd
│ ├── request_context.pyi
│ ├── request_context.pyx
│ ├── setup.py
│ ├── test/
│ │ ├── AsyncGeneratorTest.cpp
│ │ ├── BUCK
│ │ ├── ErrorTest.cpp
│ │ ├── IOBufTestUtils.cpp
│ │ ├── IOBufTestUtils.h
│ │ ├── WeakTest.cpp
│ │ ├── coro.py
│ │ ├── futures.py
│ │ ├── generator.py
│ │ ├── iobuf.py
│ │ ├── iobuf_helper.pxd
│ │ ├── iobuf_helper.pyi
│ │ ├── iobuf_helper.pyx
│ │ ├── request_context.py
│ │ ├── request_context_helper.pyi
│ │ ├── request_context_helper.pyx
│ │ ├── simple.h
│ │ ├── simplebridge.pyx
│ │ ├── simplebridgecoro.pyx
│ │ ├── simplecoro.h
│ │ ├── simplegenerator.h
│ │ ├── simplegenerator.pyx
│ │ ├── teardown.py
│ │ ├── test_set_executor.h
│ │ ├── test_set_executor.py
│ │ └── test_set_executor_cython.pyx
│ └── windows/
│ └── iocp.pyx
├── random/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── hash.h
│ ├── seed_seq.h
│ ├── test/
│ │ ├── BUCK
│ │ ├── Xoshiro256ppTest.cpp
│ │ ├── engine_bench.cpp
│ │ ├── hash_test.cpp
│ │ └── seed_seq_test.cpp
│ └── xoshiro256pp.h
├── result/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── README.md
│ ├── coded_rich_error.h
│ ├── coro.h
│ ├── demo/
│ │ ├── BUCK
│ │ └── basketball.cpp
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── immortal_exception_storage.h
│ │ ├── ptr_immortal_exception_storage.h
│ │ ├── result_or_unwind.h
│ │ ├── result_promise.h
│ │ ├── rich_error_common.h
│ │ ├── rich_exception_ptr_storage.cpp
│ │ └── rich_exception_ptr_storage.h
│ ├── docs/
│ │ ├── design_notes.md
│ │ ├── epitaphs.md
│ │ ├── future_epitaph_in_place.md
│ │ ├── future_fast_rtti.md
│ │ ├── future_ideas.md
│ │ ├── future_small_value.md
│ │ ├── result.md
│ │ ├── rich_error.md
│ │ ├── rich_error_code.md
│ │ └── rich_exception_ptr.md
│ ├── epitaph.cpp
│ ├── epitaph.h
│ ├── errc_rich_error.cpp
│ ├── errc_rich_error.h
│ ├── gtest_helpers.h
│ ├── immortal_rich_error.h
│ ├── nestable_coded_rich_error.h
│ ├── or_unwind_epitaph.h
│ ├── result.cpp
│ ├── result.h
│ ├── rich_error.h
│ ├── rich_error_base.cpp
│ ├── rich_error_base.h
│ ├── rich_error_code.cpp
│ ├── rich_error_code.h
│ ├── rich_error_fwd.h
│ ├── rich_exception_ptr.cpp
│ ├── rich_exception_ptr.h
│ ├── rich_msg.h
│ ├── test/
│ │ ├── BUCK
│ │ ├── coded_rich_error_test.cpp
│ │ ├── common.h
│ │ ├── epitaph_bench.cpp
│ │ ├── epitaph_test.cpp
│ │ ├── errc_rich_error_test.cpp
│ │ ├── immortal_rich_error_test.cpp
│ │ ├── nestable_coded_rich_error_test.cpp
│ │ ├── or_unwind_epitaph_test.cpp
│ │ ├── or_unwind_test.cpp
│ │ ├── result_bench.cpp
│ │ ├── result_coro_bench.cpp
│ │ ├── result_test.cpp
│ │ ├── rich_error_code_bench.cpp
│ │ ├── rich_error_code_test.cpp
│ │ ├── rich_error_codes.h
│ │ ├── rich_error_test.cpp
│ │ ├── rich_exception_ptr_bench.cpp
│ │ ├── rich_exception_ptr_check_get.h
│ │ ├── rich_exception_ptr_common.h
│ │ ├── rich_exception_ptr_constexpr.cpp
│ │ ├── rich_exception_ptr_fmt_bench.cpp
│ │ ├── rich_exception_ptr_fmt_test.cpp
│ │ ├── rich_exception_ptr_fundamentals_test.cpp
│ │ ├── rich_exception_ptr_immortal_test.cpp
│ │ ├── rich_exception_ptr_misc_test.cpp
│ │ ├── rich_exception_ptr_owned_test.cpp
│ │ ├── rich_msg_test.cpp
│ │ ├── stack_epitaph_test.cpp
│ │ ├── try_test.cpp
│ │ └── value_only_result_test.cpp
│ ├── try.h
│ ├── value_only_result.h
│ └── value_only_result_coro.h
├── rust/
│ ├── BUCK
│ ├── compression/
│ │ ├── BUCK
│ │ ├── compression.cpp
│ │ ├── compression.h
│ │ └── compression.rs
│ ├── dynamic/
│ │ ├── BUCK
│ │ ├── dynamic.cpp
│ │ ├── dynamic.h
│ │ └── dynamic.rs
│ ├── iobuf/
│ │ ├── BUCK
│ │ ├── iobuf.cpp
│ │ ├── iobuf.h
│ │ ├── iobuf_sys.rs
│ │ └── src/
│ │ ├── cursor.rs
│ │ ├── iobuf.rs
│ │ ├── iobufmut.rs
│ │ ├── lib.rs
│ │ └── test.rs
│ ├── lib.rs
│ ├── logging/
│ │ ├── BUCK
│ │ ├── lib.rs
│ │ ├── logging.cpp
│ │ └── logging.h
│ ├── memory/
│ │ ├── BUCK
│ │ ├── lib.rs
│ │ ├── memory.cpp
│ │ └── memory.h
│ ├── network_address/
│ │ ├── BUCK
│ │ ├── FollyWrapper.cpp
│ │ ├── FollyWrapper.h
│ │ └── src/
│ │ └── lib.rs
│ ├── request_context/
│ │ ├── BUCK
│ │ ├── benches/
│ │ │ └── request_context_tokio_bench.rs
│ │ ├── request_context.cpp
│ │ ├── request_context.h
│ │ ├── request_context.rs
│ │ ├── request_context_future.rs
│ │ └── request_context_tokio.rs
│ ├── singleton_vault/
│ │ ├── BUCK
│ │ ├── lib.rs
│ │ ├── singleton.cpp
│ │ ├── singleton.h
│ │ ├── singleton_test.cpp
│ │ ├── singleton_test.h
│ │ └── test.rs
│ ├── socket_address/
│ │ ├── BUCK
│ │ ├── RustSocketAddress.cpp
│ │ ├── RustSocketAddress.h
│ │ └── lib.rs
│ ├── string/
│ │ ├── BUCK
│ │ ├── lib.rs
│ │ ├── string.cpp
│ │ ├── string.h
│ │ └── string.rs
│ └── tdigest/
│ ├── BUCK
│ ├── tdigest.cpp
│ ├── tdigest.h
│ └── tdigest.rs
├── settings/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── CommandLineParser.cpp
│ ├── CommandLineParser.h
│ ├── Immutables.cpp
│ ├── Immutables.h
│ ├── Observer.h
│ ├── Settings.cpp
│ ├── Settings.h
│ ├── SettingsAccessorProxy.cpp
│ ├── SettingsAccessorProxy.h
│ ├── Types.cpp
│ ├── Types.h
│ ├── detail/
│ │ └── SettingsImpl.h
│ └── test/
│ ├── BUCK
│ ├── CommandLineParserTest.cpp
│ ├── SettingsBenchmarks.cpp
│ ├── SettingsTest.cpp
│ ├── a.cpp
│ ├── a.h
│ ├── b.cpp
│ └── b.h
├── small_vector.h
├── somerge_defs.bzl
├── sorted_vector_types.h
├── ssl/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── OpenSSLCertUtils.cpp
│ ├── OpenSSLCertUtils.h
│ ├── OpenSSLHash.cpp
│ ├── OpenSSLHash.h
│ ├── OpenSSLKeyUtils.cpp
│ ├── OpenSSLKeyUtils.h
│ ├── OpenSSLPtrTypes.h
│ ├── OpenSSLTicketHandler.h
│ ├── OpenSSLVersionFinder.h
│ ├── PasswordCollector.cpp
│ ├── PasswordCollector.h
│ ├── SSLSession.h
│ ├── SSLSessionManager.cpp
│ ├── SSLSessionManager.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── OpenSSLSession.cpp
│ │ └── OpenSSLSession.h
│ └── test/
│ ├── BUCK
│ ├── OpenSSLCertUtilsTest.cpp
│ ├── OpenSSLHashTest.cpp
│ ├── OpenSSLKeyUtilsTest.cpp
│ └── SSLSessionManagerTest.cpp
├── stats/
│ ├── BUCK
│ ├── BucketedTimeSeries-inl.h
│ ├── BucketedTimeSeries.h
│ ├── CMakeLists.txt
│ ├── DigestBuilder-inl.h
│ ├── DigestBuilder.h
│ ├── Histogram-inl.h
│ ├── Histogram.h
│ ├── MultiLevelTimeSeries-inl.h
│ ├── MultiLevelTimeSeries.h
│ ├── QuantileEstimator-inl.h
│ ├── QuantileEstimator.cpp
│ ├── QuantileEstimator.h
│ ├── QuantileHistogram-inl.h
│ ├── QuantileHistogram.h
│ ├── StreamingStats.h
│ ├── TDigest.cpp
│ ├── TDigest.h
│ ├── TimeseriesHistogram-inl.h
│ ├── TimeseriesHistogram.h
│ ├── detail/
│ │ ├── BUCK
│ │ ├── Bucket.h
│ │ ├── BufferedStat-inl.h
│ │ ├── BufferedStat.h
│ │ ├── CMakeLists.txt
│ │ ├── DoubleRadixSort.cpp
│ │ ├── DoubleRadixSort.h
│ │ ├── SlidingWindow-inl.h
│ │ ├── SlidingWindow.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── DoubleRadixSortTest.cpp
│ └── test/
│ ├── BUCK
│ ├── BucketedTimeSeriesBenchmark.cpp
│ ├── BufferedStatTest.cpp
│ ├── DigestBuilderBenchmark.cpp
│ ├── DigestBuilderTest.cpp
│ ├── HistogramBenchmark.cpp
│ ├── HistogramTest.cpp
│ ├── MultiLevelTimeSeriesBenchmark.cpp
│ ├── QuantileEstimatorBenchmark.cpp
│ ├── QuantileEstimatorTest.cpp
│ ├── QuantileHistogramBenchmark.cpp
│ ├── QuantileHistogramTest.cpp
│ ├── SlidingWindowTest.cpp
│ ├── StreamingStatsTest.cpp
│ ├── TDigestBenchmark.cpp
│ ├── TDigestTest.cpp
│ ├── TimeSeriesTest.cpp
│ └── TimeseriesHistogramTest.cpp
├── stop_watch.h
├── support/
│ ├── gdb.py
│ └── test/
│ ├── BUCK
│ ├── GdbUtil.h
│ ├── main.cpp
│ └── runtest.gdb
├── synchronization/
│ ├── AsymmetricThreadFence.cpp
│ ├── AsymmetricThreadFence.h
│ ├── AtomicNotification-inl.h
│ ├── AtomicNotification.cpp
│ ├── AtomicNotification.h
│ ├── AtomicRef.h
│ ├── AtomicStruct.h
│ ├── AtomicUtil-inl.h
│ ├── AtomicUtil.h
│ ├── BUCK
│ ├── Baton.h
│ ├── CMakeLists.txt
│ ├── CallOnce.h
│ ├── DelayedInit.h
│ ├── DistributedMutex-inl.h
│ ├── DistributedMutex.cpp
│ ├── DistributedMutex.h
│ ├── EventCount.h
│ ├── FlatCombining.h
│ ├── Hazptr-fwd.h
│ ├── Hazptr.cpp
│ ├── Hazptr.h
│ ├── HazptrDomain.cpp
│ ├── HazptrDomain.h
│ ├── HazptrHolder.h
│ ├── HazptrObj.h
│ ├── HazptrObjLinked.h
│ ├── HazptrRec.h
│ ├── HazptrThrLocal.h
│ ├── HazptrThreadPoolExecutor.cpp
│ ├── HazptrThreadPoolExecutor.h
│ ├── Latch.h
│ ├── LifoSem.h
│ ├── Lock.h
│ ├── MicroSpinLock.h
│ ├── NativeSemaphore.h
│ ├── ParkingLot.cpp
│ ├── ParkingLot.h
│ ├── PicoSpinLock.h
│ ├── RWSpinLock.h
│ ├── Rcu.cpp
│ ├── Rcu.h
│ ├── RelaxedAtomic.h
│ ├── SanitizeThread.cpp
│ ├── SanitizeThread.h
│ ├── SaturatingSemaphore.h
│ ├── SmallLocks.h
│ ├── StripedThrottledLifoSem.cpp
│ ├── StripedThrottledLifoSem.h
│ ├── ThrottledLifoSem.h
│ ├── WaitOptions.h
│ ├── detail/
│ │ ├── AtomicUtils.h
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── HazptrUtils.h
│ │ ├── InlineFunctionRef.h
│ │ ├── Sleeper.cpp
│ │ ├── Sleeper.h
│ │ ├── Spin.h
│ │ ├── ThreadCachedLists.h
│ │ ├── ThreadCachedReaders.h
│ │ ├── ThreadCachedTag.h
│ │ └── test/
│ │ ├── BUCK
│ │ └── InlineFunctionRefTest.cpp
│ ├── example/
│ │ ├── BUCK
│ │ ├── HazptrLockFreeLIFO.h
│ │ ├── HazptrSWMRSet.h
│ │ └── HazptrWideCAS.h
│ └── test/
│ ├── AtomicNotificationTest.cpp
│ ├── AtomicRefTest.cpp
│ ├── AtomicStructTest.cpp
│ ├── AtomicUtilBench.cpp
│ ├── AtomicUtilTest.cpp
│ ├── BUCK
│ ├── Barrier.h
│ ├── BarrierTest.cpp
│ ├── BatonBenchmark.cpp
│ ├── BatonTest.cpp
│ ├── BatonTestHelpers.h
│ ├── CallOnceBenchmark.cpp
│ ├── CallOnceTest.cpp
│ ├── DelayedInitTest.cpp
│ ├── DistributedMutexTest.cpp
│ ├── EventCountTest.cpp
│ ├── FlatCombiningBenchmark.cpp
│ ├── FlatCombiningExamples.h
│ ├── FlatCombiningTest.cpp
│ ├── FlatCombiningTestHelpers.h
│ ├── HazptrBench.cpp
│ ├── HazptrTest.cpp
│ ├── LatchTest.cpp
│ ├── LifoSemBench.cpp
│ ├── LifoSemTests.cpp
│ ├── LockTest.cpp
│ ├── NativeSemaphoreTest.cpp
│ ├── ParkingLotBenchmark.cpp
│ ├── ParkingLotTest.cpp
│ ├── RWSpinLockTest.cpp
│ ├── RcuBench.cpp
│ ├── RcuTest.cpp
│ ├── RelaxedAtomicTest.cpp
│ ├── SaturatingSemaphoreTest.cpp
│ ├── Semaphore.h
│ ├── SemaphoreTest.cpp
│ ├── SmallLocksBenchmark.cpp
│ ├── SmallLocksTest.cpp
│ ├── StripedThrottledLifoSemTest.cpp
│ ├── ThreadCachedEpochBench.h
│ ├── ThreadCachedReadersBench.cpp
│ ├── ThreadCachedReadersTest.cpp
│ └── ThrottledLifoSemTest.cpp
├── system/
│ ├── AtFork.cpp
│ ├── AtFork.h
│ ├── AuxVector.h
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── EnvUtil.cpp
│ ├── EnvUtil.h
│ ├── HardwareConcurrency.cpp
│ ├── HardwareConcurrency.h
│ ├── MemoryMapping.cpp
│ ├── MemoryMapping.h
│ ├── Pid.cpp
│ ├── Pid.h
│ ├── Shell.cpp
│ ├── Shell.h
│ ├── ThreadId.cpp
│ ├── ThreadId.h
│ ├── ThreadName.cpp
│ ├── ThreadName.h
│ ├── arch/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── test/
│ │ │ ├── BUCK
│ │ │ └── x86_test.cpp
│ │ └── x86.h
│ ├── os/
│ │ ├── BUCK
│ │ ├── CMakeLists.txt
│ │ ├── linux.cpp
│ │ └── linux.h
│ └── test/
│ ├── AtForkTest.cpp
│ ├── AuxVectorTest.cpp
│ ├── BUCK
│ ├── EnvUtilTest.cpp
│ ├── HardwareConcurrencyTest.cpp
│ ├── MemoryMappingTest.cpp
│ ├── PidTest.cpp
│ ├── ShellTest.cpp
│ ├── ThreadIdTest.cpp
│ └── ThreadNameTest.cpp
├── test/
│ ├── AHMIntStressTest.cpp
│ ├── ArenaSmartPtrTest.cpp
│ ├── AsciiCaseInsensitiveBenchmark.cpp
│ ├── AsciiCaseInsensitiveTest.cpp
│ ├── AtomicHashArrayTest.cpp
│ ├── AtomicHashMapTest.cpp
│ ├── AtomicLinkedListTest.cpp
│ ├── AtomicUnorderedMapTest.cpp
│ ├── BUCK
│ ├── BenchmarkIntegrationTest.cpp
│ ├── BenchmarkTest.cpp
│ ├── BenchmarkUtilTest.cpp
│ ├── BufferedAtomic.cpp
│ ├── BufferedAtomic.h
│ ├── BufferedAtomicTest.cpp
│ ├── CancellationTokenBench.cpp
│ ├── CancellationTokenTest.cpp
│ ├── ChronoBench.cpp
│ ├── ChronoTest.cpp
│ ├── ClockGettimeWrappersTest.cpp
│ ├── ComparisonOperatorTestUtil.h
│ ├── ConcurrentBitSetTest.cpp
│ ├── ConcurrentLazyTest.cpp
│ ├── ConcurrentSkipListBenchmark.cpp
│ ├── ConcurrentSkipListTest.cpp
│ ├── ConstexprMathBenchmark.cpp
│ ├── ConstexprMathTest.cpp
│ ├── ConstructorCallbackListTest.cpp
│ ├── ConvBenchmark.cpp
│ ├── ConvTest.cpp
│ ├── CpuIdTest.cpp
│ ├── DemangleTest.cpp
│ ├── DeterministicSchedule.cpp
│ ├── DeterministicSchedule.h
│ ├── DeterministicScheduleTest.cpp
│ ├── DiscriminatedPtrTest.cpp
│ ├── EndianTest.cpp
│ ├── ExceptionStringTest.cpp
│ ├── ExceptionTest.cpp
│ ├── ExceptionWrapperBenchmark.cpp
│ ├── ExceptionWrapperTest.cpp
│ ├── ExecutorTest.cpp
│ ├── ExpectedCoroutinesBench.cpp
│ ├── ExpectedCoroutinesTest.cpp
│ ├── ExpectedTest.cpp
│ ├── FBStringBenchmark.cpp
│ ├── FBStringTest.cpp
│ ├── FBStringTestBenchmarks.cpp.h
│ ├── FBVectorTestUtil.cpp
│ ├── FBVectorTestUtil.h
│ ├── FileLockTest.cpp
│ ├── FileTest.cpp
│ ├── FileTestLockHelper.cpp
│ ├── FileUtilTest.cpp
│ ├── FingerprintBenchmark.cpp
│ ├── FingerprintTest.cpp
│ ├── FixedStringTest.cpp
│ ├── FmtUtilityTest.cpp
│ ├── FormatBenchmark.cpp
│ ├── FormatOtherTest.cpp
│ ├── FormatTest.cpp
│ ├── FunctionRefBenchmark.cpp
│ ├── FunctionRefTest.cpp
│ ├── FunctionTest.cpp
│ ├── FutexTest.cpp
│ ├── GLogBenchmark.cpp
│ ├── GLogTest.cpp
│ ├── GroupVarintTest.cpp
│ ├── IPAddressBenchmark.cpp
│ ├── IPAddressTest.cpp
│ ├── IndestructibleTest.cpp
│ ├── IndexedMemPoolTest.cpp
│ ├── IteratorsTest.cpp
│ ├── JsonMockUtil.h
│ ├── JsonTestUtil.h
│ ├── LazyTest.cpp
│ ├── MPMCPipelineTest.cpp
│ ├── MPMCQueueTest.cpp
│ ├── MacAddressTest.cpp
│ ├── MathBenchmark.cpp
│ ├── MathTest.cpp
│ ├── MaybeManagedPtrTest.cpp
│ ├── MemcpyBenchmark.cpp
│ ├── MemcpyTest.cpp
│ ├── MemcpyUseTest.cpp
│ ├── MemoryIdlerBenchmark.cpp
│ ├── MemoryIdlerTest.cpp
│ ├── MemoryTest.cpp
│ ├── MemsetBenchmark.cpp
│ ├── MemsetTest.cpp
│ ├── MoveWrapperTest.cpp
│ ├── ObserverContainerTest.cpp
│ ├── ObserverContainerTestUtil.h
│ ├── OptionalCoroutinesTest.cpp
│ ├── OptionalTest.cpp
│ ├── OverloadTest.cpp
│ ├── PackedSyncPtrTest.cpp
│ ├── PaddedTest.cpp
│ ├── PolyTest.cpp
│ ├── PortabilityTest.cpp
│ ├── ProducerConsumerQueueBenchmark.cpp
│ ├── ProducerConsumerQueueTest.cpp
│ ├── RandomBenchmark.cpp
│ ├── RandomTest.cpp
│ ├── RangeFindBenchmark.cpp
│ ├── RangeTest.cpp
│ ├── ReplaceableTest.cpp
│ ├── ScopeGuardTest.cpp
│ ├── SharedMutexTest.cpp
│ ├── SingletonBenchmark.cpp
│ ├── SingletonTest.cpp
│ ├── SingletonTestGlobal.cpp
│ ├── SingletonTestStructs.cpp
│ ├── SingletonTestStructs.h
│ ├── SingletonThreadLocalTest.cpp
│ ├── SingletonThreadLocalTestOverload.cpp
│ ├── SocketAddressTest.cpp
│ ├── SocketAddressTestHelper.cpp
│ ├── SocketAddressTestHelper.h
│ ├── SpinLockTest.cpp
│ ├── StringBenchmark.cpp
│ ├── StringTest.cpp
│ ├── StringToFloatBenchmark.cpp
│ ├── SubprocessBench.cpp
│ ├── SubprocessTest.cpp
│ ├── SubprocessTestParentDeathHelper.cpp
│ ├── SynchronizedBenchmark.cpp
│ ├── SynchronizedPtrTest.cpp
│ ├── SynchronizedTest.cpp
│ ├── SynchronizedTestLib-inl.h
│ ├── SynchronizedTestLib.h
│ ├── TestUtils.h
│ ├── TestUtilsTest.cpp
│ ├── ThreadCachedIntTest.cpp
│ ├── ThreadLocalAccessBenchmark.cpp
│ ├── ThreadLocalBenchmark.cpp
│ ├── ThreadLocalDestroyBenchmark.cpp
│ ├── ThreadLocalTest.cpp
│ ├── ThreadLocalTestLib.cpp
│ ├── TimeoutQueueTest.cpp
│ ├── TokenBucketTest.cpp
│ ├── TokenBucketTest.h
│ ├── TraitsTest.cpp
│ ├── TryTest.cpp
│ ├── UTF8StringTest.cpp
│ ├── UnicodeTest.cpp
│ ├── UnitTest.cpp
│ ├── UriBenchmark.cpp
│ ├── UriTest.cpp
│ ├── UtilityTest.cpp
│ ├── VarintTest.cpp
│ ├── base64_test.cpp
│ ├── common/
│ │ ├── BUCK
│ │ └── TestMain.cpp
│ └── stl_tests/
│ ├── BUCK
│ └── StlVectorTest.cpp
├── testing/
│ ├── BUCK
│ ├── CMakeLists.txt
│ ├── TestUtil.cpp
│ ├── TestUtil.h
│ └── test/
│ ├── BUCK
│ └── TestUtilTest.cpp
├── tool/
│ ├── BUCK
│ └── BenchmarkCompare.cpp
└── tracing/
├── AsyncStack-inl.h
├── AsyncStack.cpp
├── AsyncStack.h
├── BUCK
├── CMakeLists.txt
├── README.md
├── ScopedTraceSection.h
├── StaticTracepoint-ELF.h
├── StaticTracepoint.h
└── test/
├── AsyncStackTest.cpp
├── BUCK
├── StaticTracepointSectionTest.cpp
├── StaticTracepointSectionTest.lds
├── StaticTracepointTest.cpp
├── StaticTracepointTestModule.cpp
└── StaticTracepointTestModule.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .buckconfig
================================================
[cells]
gh_facebook_folly = .
shim_DISABLED = shim
[cell_aliases]
root = gh_facebook_folly
[oss]
internal_cell = fbcode
project_dirs = folly
[buck2]
directories_to_allow_relative_paths = //folly
================================================
FILE: .buckconfig.d/common.buckconfig
================================================
[cells]
prelude = prelude
none = none
[cell_aliases]
config = prelude
ovr_config = prelude
bazel_skylib = gh_facebook_buck2_shims_meta
buck = gh_facebook_buck2_shims_meta
fbcode = gh_facebook_buck2_shims_meta
fbcode_macros = gh_facebook_buck2_shims_meta
fbsource = gh_facebook_buck2_shims_meta
shim = gh_facebook_buck2_shims_meta
toolchains = gh_facebook_buck2_shims_meta
[external_cells]
prelude = bundled
[build]
execution_platforms = prelude//platforms:default
[parser]
target_platform_detector_spec = target:root//...->prelude//platforms:default target:shim//...->prelude//platforms:default
================================================
FILE: .buckroot
================================================
================================================
FILE: .devcontainer/Containerfile
================================================
FROM mcr.microsoft.com/devcontainers/base:jammy
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y --no-install-recommends \
watchman \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "Ubuntu",
"build": {
"cacheFrom": "ghcr.io/marinatedconcrete/config-devcontainer",
"dockerfile": "Containerfile"
},
"customizations": {
"vscode": {
"extensions": [
"GitHub.vscode-github-actions"
]
}
},
"features": {
"ghcr.io/facebook/devcontainers/features/dotslash:1": {}
}
}
================================================
FILE: .github/dependabot.yml
================================================
---
version: 2
updates:
- package-ecosystem: "devcontainers"
directory: "/"
schedule:
interval: weekly
================================================
FILE: .github/scripts/bad_targets
================================================
# This is a list of bad targets that do not build in OSS. Ideally this list goes away
# with time.
root//folly/debugging/exception_tracer:exception_counter
root//folly/debugging/exception_tracer:exception_tracer
root//folly/debugging/exception_tracer:exception_tracer_callbacks
root//folly/debugging/exception_tracer:smart_exception_stack_trace_hooks
root//folly/debugging/exception_tracer:smart_exception_tracer
root//folly/debugging/symbolizer/tool:folly-addr2line
root//folly/debugging/symbolizer/tool:libFollySegFault.so
root//folly/docs/examples/folly:baton_demo
root//folly/docs/examples/folly:cancellation_callback_demo
root//folly/docs/examples/folly:cancellation_source_demo
root//folly/docs/examples/folly:cancellation_token_demo
root//folly/docs/examples/folly:dynamic_converter_demo
root//folly/docs/examples/folly:dynamic_demo
root//folly/docs/examples/folly:executor_guide
root//folly/docs/examples/folly:file_demo
root//folly/docs/examples/folly:format_demo
root//folly/docs/examples/folly:function_demo
root//folly/docs/examples/folly:ipaddress_demo
root//folly/docs/examples/folly:likely_demo
root//folly/docs/examples/folly:map_util_demo
root//folly/docs/examples/folly:scope_guard_demo
root//folly/docs/examples/folly:scope_guard2_demo
root//folly/docs/examples/folly:scoped_event_base_thread_demo
root//folly/docs/examples/folly:scoped_event_base_thread2_demo
root//folly/docs/examples/folly:synchronized_demo
root//folly/docs/examples/folly/container:array_demo
root//folly/docs/examples/folly/dynamic:array_demo
root//folly/docs/examples/folly/dynamic:object_demo
root//folly/docs/examples/folly/coro:async_scope_demo
root//folly/docs/examples/folly/coro:cancellable_async_scope_demo
root//folly/docs/examples/folly/coro:detach_on_cancel_demo
root//folly/docs/examples/folly/coro:promise_demo
root//folly/docs/examples/folly/coro:retry_demo
root//folly/docs/examples/folly/coro:task_demo
root//folly/docs/examples/folly/coro:with_cancellation_demo
root//folly/docs/examples/folly/hash:hash_demo
root//folly/docs/examples/folly/io:i_o_buf_demo
root//folly/logging/example:example
root//folly/python:executor_lib
root//folly/python:fibers_lib
root//folly/python:iobuf_lib
root//folly/tool:benchmark_compare
================================================
FILE: .github/scripts/buck_build_and_test.sh
================================================
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
TARGETS_FILE=$(mktemp)
./buck2 targets //... | grep -F -v -f .github/scripts/bad_targets | grep -v test >"$TARGETS_FILE"
./buck2 build @"$TARGETS_FILE"
# ./buck2 test @"$TARGETS_FILE"
================================================
FILE: .github/workflows/TagIt.yml
================================================
on:
push:
tags:
# Only match TagIt tags, which always start with this prefix
- 'v20*'
name: TagIt
permissions:
contents: read
jobs:
build:
permissions:
contents: write # for actions/create-release to create a release
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Archive project
id: archive_project
run: |
FILE_NAME=${GITHUB_REPOSITORY#*/}-${GITHUB_REF##*/}
git archive ${{ github.ref }} -o ${FILE_NAME}.zip
git archive ${{ github.ref }} -o ${FILE_NAME}.tar.gz
echo "file_name=${FILE_NAME}" >> $GITHUB_OUTPUT
- name: Compute digests
id: compute_digests
run: |
echo "tgz_256=$(openssl dgst -sha256 ${{ steps.archive_project.outputs.file_name }}.tar.gz)" >> $GITHUB_OUTPUT
echo "tgz_512=$(openssl dgst -sha512 ${{ steps.archive_project.outputs.file_name }}.tar.gz)" >> $GITHUB_OUTPUT
echo "zip_256=$(openssl dgst -sha256 ${{ steps.archive_project.outputs.file_name }}.zip)" >> $GITHUB_OUTPUT
echo "zip_512=$(openssl dgst -sha512 ${{ steps.archive_project.outputs.file_name }}.zip)" >> $GITHUB_OUTPUT
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body: |
Automated release from TagIt
File Hashes
- ${{ steps.compute_digests.outputs.zip_256 }}
- ${{ steps.compute_digests.outputs.zip_512 }}
- ${{ steps.compute_digests.outputs.tgz_256 }}
- ${{ steps.compute_digests.outputs.tgz_512 }}
draft: false
prerelease: false
- name: Upload zip
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./${{ steps.archive_project.outputs.file_name }}.zip
asset_name: ${{ steps.archive_project.outputs.file_name }}.zip
asset_content_type: application/zip
- name: Upload tar.gz
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./${{ steps.archive_project.outputs.file_name }}.tar.gz
asset_name: ${{ steps.archive_project.outputs.file_name }}.tar.gz
asset_content_type: application/gzip
================================================
FILE: .github/workflows/devcontainer.yml
================================================
---
name: Development Container-Based CI
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
jobs:
build:
permissions:
contents: read
packages: write
runs-on: ${{ matrix.runsOn }}
strategy:
matrix:
runsOn:
- ubuntu-24.04-arm
- ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Login to GitHub Container Registry
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker-container
- if: ${{ github.event_name != 'pull_request' }}
id: cache_to_helper
run: echo "cacheTo=ghcr.io/${{ github.repository_owner }}/devcontainers/folly" >> $GITHUB_OUTPUT
- name: Build devcontainer image
uses: devcontainers/ci@8bf61b26e9c3a98f69cb6ce2f88d24ff59b785c6 # v0.3.1900000417
with:
cacheFrom: ghcr.io/${{ github.repository_owner }}/devcontainers/folly
cacheTo: ${{ steps.cache_to_helper.outputs.cacheTo }}
env: |
CI=1
imageName: ghcr.io/${{ github.repository_owner }}/devcontainers/folly
push: filter
refFilterForPush: refs/heads/main
================================================
FILE: .github/workflows/getdeps_linux.yml
================================================
# This file was @generated by getdeps.py
name: linux
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- name: Show disk space at start
run: df -h
- name: Free up disk space
run: sudo rm -rf /usr/local/lib/android
- name: Show disk space after freeing up
run: df -h
- name: Update system package info
run: sudo --preserve-env=http_proxy apt-get update
- name: Install system deps
run: sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive folly && sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive patchelf
- id: paths
name: Query paths
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages query-paths --recursive --src-dir=. folly >> "$GITHUB_OUTPUT"
- name: Fetch boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests boost
- name: Fetch libaio
if: ${{ steps.paths.outputs.libaio_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libaio
- name: Fetch ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests ninja
- name: Fetch cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests cmake
- name: Fetch double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests double-conversion
- name: Fetch fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fast_float
- name: Fetch fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fmt
- name: Fetch gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests gflags
- name: Fetch glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests glog
- name: Fetch googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests googletest
- name: Fetch libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libdwarf
- name: Fetch libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libevent
- name: Fetch zlib
if: ${{ steps.paths.outputs.zlib_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zlib
- name: Fetch lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests lz4
- name: Fetch snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests snappy
- name: Fetch zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zstd
- name: Fetch autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests autoconf
- name: Fetch automake
if: ${{ steps.paths.outputs.automake_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests automake
- name: Fetch libtool
if: ${{ steps.paths.outputs.libtool_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libtool
- name: Fetch libiberty
if: ${{ steps.paths.outputs.libiberty_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libiberty
- name: Fetch libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libsodium
- name: Fetch libunwind
if: ${{ steps.paths.outputs.libunwind_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libunwind
- name: Fetch xz
if: ${{ steps.paths.outputs.xz_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests xz
- name: Restore boost from cache
id: restore_boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Build boost
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests boost
- name: Save boost to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Restore libaio from cache
id: restore_libaio
if: ${{ steps.paths.outputs.libaio_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libaio_INSTALL }}
key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install
- name: Build libaio
if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libaio
- name: Save libaio to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libaio_INSTALL }}
key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install
- name: Restore ninja from cache
id: restore_ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Build ninja
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests ninja
- name: Save ninja to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Restore cmake from cache
id: restore_cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Build cmake
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests cmake
- name: Save cmake to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Restore double-conversion from cache
id: restore_double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Build double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests double-conversion
- name: Save double-conversion to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Restore fast_float from cache
id: restore_fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Build fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fast_float
- name: Save fast_float to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Restore fmt from cache
id: restore_fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Build fmt
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fmt
- name: Save fmt to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Restore gflags from cache
id: restore_gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Build gflags
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests gflags
- name: Save gflags to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Restore glog from cache
id: restore_glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Build glog
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests glog
- name: Save glog to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Restore googletest from cache
id: restore_googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Build googletest
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests googletest
- name: Save googletest to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Restore libdwarf from cache
id: restore_libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Build libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libdwarf
- name: Save libdwarf to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Restore libevent from cache
id: restore_libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Build libevent
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libevent
- name: Save libevent to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Restore zlib from cache
id: restore_zlib
if: ${{ steps.paths.outputs.zlib_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.zlib_INSTALL }}
key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install
- name: Build zlib
if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests zlib
- name: Save zlib to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.zlib_INSTALL }}
key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install
- name: Restore lz4 from cache
id: restore_lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Build lz4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests lz4
- name: Save lz4 to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Restore snappy from cache
id: restore_snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Build snappy
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests snappy
- name: Save snappy to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Restore zstd from cache
id: restore_zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Build zstd
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests zstd
- name: Save zstd to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Restore autoconf from cache
id: restore_autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.autoconf_INSTALL }}
key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install
- name: Build autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests autoconf
- name: Save autoconf to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.autoconf_INSTALL }}
key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install
- name: Restore automake from cache
id: restore_automake
if: ${{ steps.paths.outputs.automake_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.automake_INSTALL }}
key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install
- name: Build automake
if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests automake
- name: Save automake to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.automake_INSTALL }}
key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install
- name: Restore libtool from cache
id: restore_libtool
if: ${{ steps.paths.outputs.libtool_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libtool_INSTALL }}
key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install
- name: Build libtool
if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libtool
- name: Save libtool to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libtool_INSTALL }}
key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install
- name: Restore libiberty from cache
id: restore_libiberty
if: ${{ steps.paths.outputs.libiberty_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libiberty_INSTALL }}
key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install
- name: Build libiberty
if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libiberty
- name: Save libiberty to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libiberty_INSTALL }}
key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install
- name: Restore libsodium from cache
id: restore_libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Build libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libsodium
- name: Save libsodium to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Restore libunwind from cache
id: restore_libunwind
if: ${{ steps.paths.outputs.libunwind_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libunwind_INSTALL }}
key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install
- name: Build libunwind
if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libunwind
- name: Save libunwind to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libunwind_INSTALL }}
key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install
- name: Restore xz from cache
id: restore_xz
if: ${{ steps.paths.outputs.xz_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.xz_INSTALL }}
key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install
- name: Build xz
if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests xz
- name: Save xz to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.xz_INSTALL }}
key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install
- name: Build folly
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --src-dir=. folly --project-install-prefix folly:/usr/local
- name: Copy artifacts
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fixup-dyn-deps --strip --src-dir=. folly _artifacts/linux --project-install-prefix folly:/usr/local --final-install-prefix /usr/local
- uses: actions/upload-artifact@v6
with:
name: folly
path: _artifacts
- name: Test folly
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages test --src-dir=. folly --project-install-prefix folly:/usr/local
- name: Show disk space at end
if: always()
run: df -h
================================================
FILE: .github/workflows/getdeps_mac.yml
================================================
# This file was @generated by getdeps.py
name: mac
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v6
- name: Show disk space at start
run: df -h
- name: Free up disk space
run: sudo rm -rf /usr/local/lib/android
- name: Show disk space after freeing up
run: df -h
- name: Install system deps
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive folly
- id: paths
name: Query paths
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages query-paths --recursive --src-dir=. folly >> "$GITHUB_OUTPUT"
- name: Fetch boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests boost
- name: Fetch openssl
if: ${{ steps.paths.outputs.openssl_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests openssl
- name: Fetch ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests ninja
- name: Fetch cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests cmake
- name: Fetch double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests double-conversion
- name: Fetch fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fast_float
- name: Fetch fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fmt
- name: Fetch gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests gflags
- name: Fetch glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests glog
- name: Fetch googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests googletest
- name: Fetch libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libdwarf
- name: Fetch libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libevent
- name: Fetch lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests lz4
- name: Fetch snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests snappy
- name: Fetch zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zstd
- name: Fetch autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests autoconf
- name: Fetch automake
if: ${{ steps.paths.outputs.automake_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests automake
- name: Fetch libtool
if: ${{ steps.paths.outputs.libtool_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libtool
- name: Fetch libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libsodium
- name: Fetch xz
if: ${{ steps.paths.outputs.xz_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests xz
- name: Restore boost from cache
id: restore_boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Build boost
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests boost
- name: Save boost to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Restore openssl from cache
id: restore_openssl
if: ${{ steps.paths.outputs.openssl_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.openssl_INSTALL }}
key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install
- name: Build openssl
if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests openssl
- name: Save openssl to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.openssl_INSTALL }}
key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install
- name: Restore ninja from cache
id: restore_ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Build ninja
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests ninja
- name: Save ninja to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Restore cmake from cache
id: restore_cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Build cmake
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests cmake
- name: Save cmake to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Restore double-conversion from cache
id: restore_double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Build double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests double-conversion
- name: Save double-conversion to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Restore fast_float from cache
id: restore_fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Build fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fast_float
- name: Save fast_float to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Restore fmt from cache
id: restore_fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Build fmt
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests fmt
- name: Save fmt to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Restore gflags from cache
id: restore_gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Build gflags
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests gflags
- name: Save gflags to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Restore glog from cache
id: restore_glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Build glog
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests glog
- name: Save glog to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Restore googletest from cache
id: restore_googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Build googletest
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests googletest
- name: Save googletest to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Restore libdwarf from cache
id: restore_libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Build libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libdwarf
- name: Save libdwarf to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Restore libevent from cache
id: restore_libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Build libevent
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libevent
- name: Save libevent to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Restore lz4 from cache
id: restore_lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Build lz4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests lz4
- name: Save lz4 to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Restore snappy from cache
id: restore_snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Build snappy
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests snappy
- name: Save snappy to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Restore zstd from cache
id: restore_zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Build zstd
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests zstd
- name: Save zstd to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Restore autoconf from cache
id: restore_autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.autoconf_INSTALL }}
key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install
- name: Build autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests autoconf
- name: Save autoconf to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.autoconf_INSTALL }}
key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install
- name: Restore automake from cache
id: restore_automake
if: ${{ steps.paths.outputs.automake_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.automake_INSTALL }}
key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install
- name: Build automake
if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests automake
- name: Save automake to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.automake_INSTALL }}
key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install
- name: Restore libtool from cache
id: restore_libtool
if: ${{ steps.paths.outputs.libtool_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libtool_INSTALL }}
key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install
- name: Build libtool
if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libtool
- name: Save libtool to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libtool_INSTALL }}
key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install
- name: Restore libsodium from cache
id: restore_libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Build libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests libsodium
- name: Save libsodium to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Restore xz from cache
id: restore_xz
if: ${{ steps.paths.outputs.xz_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.xz_INSTALL }}
key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install
- name: Build xz
if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --free-up-disk --no-tests xz
- name: Save xz to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.xz_INSTALL }}
key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install
- name: Build folly
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --src-dir=. folly --project-install-prefix folly:/usr/local
- name: Copy artifacts
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fixup-dyn-deps --src-dir=. folly _artifacts/mac --project-install-prefix folly:/usr/local --final-install-prefix /usr/local
- uses: actions/upload-artifact@v6
with:
name: folly
path: _artifacts
- name: Test folly
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages test --src-dir=. folly --project-install-prefix folly:/usr/local
- name: Show disk space at end
if: always()
run: df -h
================================================
FILE: .github/workflows/getdeps_shared-libs_linux.yml
================================================
# This file was @generated by getdeps.py
name: Shared Libs Linux
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- name: Show disk space at start
run: df -h
- name: Free up disk space
run: sudo rm -rf /usr/local/lib/android
- name: Show disk space after freeing up
run: df -h
- name: Update system package info
run: sudo --preserve-env=http_proxy apt-get update
- name: Install system deps
run: sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive folly && sudo --preserve-env=http_proxy python3 build/fbcode_builder/getdeps.py --allow-system-packages install-system-deps --recursive patchelf
- id: paths
name: Query paths
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages query-paths --recursive --src-dir=. folly >> "$GITHUB_OUTPUT"
- name: Fetch boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests boost
- name: Fetch libaio
if: ${{ steps.paths.outputs.libaio_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libaio
- name: Fetch ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests ninja
- name: Fetch cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests cmake
- name: Fetch double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests double-conversion
- name: Fetch fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fast_float
- name: Fetch fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests fmt
- name: Fetch gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests gflags
- name: Fetch glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests glog
- name: Fetch googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests googletest
- name: Fetch libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libdwarf
- name: Fetch libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libevent
- name: Fetch zlib
if: ${{ steps.paths.outputs.zlib_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zlib
- name: Fetch lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests lz4
- name: Fetch snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests snappy
- name: Fetch zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests zstd
- name: Fetch autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests autoconf
- name: Fetch automake
if: ${{ steps.paths.outputs.automake_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests automake
- name: Fetch libtool
if: ${{ steps.paths.outputs.libtool_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libtool
- name: Fetch libiberty
if: ${{ steps.paths.outputs.libiberty_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libiberty
- name: Fetch libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libsodium
- name: Fetch libunwind
if: ${{ steps.paths.outputs.libunwind_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests libunwind
- name: Fetch xz
if: ${{ steps.paths.outputs.xz_SOURCE }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fetch --no-tests xz
- name: Restore boost from cache
id: restore_boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Build boost
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests boost
- name: Save boost to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Restore libaio from cache
id: restore_libaio
if: ${{ steps.paths.outputs.libaio_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libaio_INSTALL }}
key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install
- name: Build libaio
if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libaio
- name: Save libaio to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libaio_SOURCE && ! steps.restore_libaio.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libaio_INSTALL }}
key: ${{ steps.paths.outputs.libaio_CACHE_KEY }}-install
- name: Restore ninja from cache
id: restore_ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Build ninja
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests ninja
- name: Save ninja to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Restore cmake from cache
id: restore_cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Build cmake
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests cmake
- name: Save cmake to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Restore double-conversion from cache
id: restore_double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Build double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests double-conversion
- name: Save double-conversion to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Restore fast_float from cache
id: restore_fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Build fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests fast_float
- name: Save fast_float to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Restore fmt from cache
id: restore_fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Build fmt
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests fmt
- name: Save fmt to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Restore gflags from cache
id: restore_gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Build gflags
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests gflags
- name: Save gflags to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Restore glog from cache
id: restore_glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Build glog
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests glog
- name: Save glog to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Restore googletest from cache
id: restore_googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Build googletest
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests googletest
- name: Save googletest to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Restore libdwarf from cache
id: restore_libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Build libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libdwarf
- name: Save libdwarf to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Restore libevent from cache
id: restore_libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Build libevent
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libevent
- name: Save libevent to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Restore zlib from cache
id: restore_zlib
if: ${{ steps.paths.outputs.zlib_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.zlib_INSTALL }}
key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install
- name: Build zlib
if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests zlib
- name: Save zlib to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.zlib_INSTALL }}
key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install
- name: Restore lz4 from cache
id: restore_lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Build lz4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests lz4
- name: Save lz4 to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Restore snappy from cache
id: restore_snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Build snappy
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests snappy
- name: Save snappy to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Restore zstd from cache
id: restore_zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Build zstd
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests zstd
- name: Save zstd to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Restore autoconf from cache
id: restore_autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.autoconf_INSTALL }}
key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install
- name: Build autoconf
if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests autoconf
- name: Save autoconf to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.autoconf_SOURCE && ! steps.restore_autoconf.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.autoconf_INSTALL }}
key: ${{ steps.paths.outputs.autoconf_CACHE_KEY }}-install
- name: Restore automake from cache
id: restore_automake
if: ${{ steps.paths.outputs.automake_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.automake_INSTALL }}
key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install
- name: Build automake
if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests automake
- name: Save automake to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.automake_SOURCE && ! steps.restore_automake.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.automake_INSTALL }}
key: ${{ steps.paths.outputs.automake_CACHE_KEY }}-install
- name: Restore libtool from cache
id: restore_libtool
if: ${{ steps.paths.outputs.libtool_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libtool_INSTALL }}
key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install
- name: Build libtool
if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libtool
- name: Save libtool to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libtool_SOURCE && ! steps.restore_libtool.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libtool_INSTALL }}
key: ${{ steps.paths.outputs.libtool_CACHE_KEY }}-install
- name: Restore libiberty from cache
id: restore_libiberty
if: ${{ steps.paths.outputs.libiberty_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libiberty_INSTALL }}
key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install
- name: Build libiberty
if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libiberty
- name: Save libiberty to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libiberty_SOURCE && ! steps.restore_libiberty.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libiberty_INSTALL }}
key: ${{ steps.paths.outputs.libiberty_CACHE_KEY }}-install
- name: Restore libsodium from cache
id: restore_libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Build libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libsodium
- name: Save libsodium to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Restore libunwind from cache
id: restore_libunwind
if: ${{ steps.paths.outputs.libunwind_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libunwind_INSTALL }}
key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install
- name: Build libunwind
if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests libunwind
- name: Save libunwind to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libunwind_SOURCE && ! steps.restore_libunwind.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libunwind_INSTALL }}
key: ${{ steps.paths.outputs.libunwind_CACHE_KEY }}-install
- name: Restore xz from cache
id: restore_xz
if: ${{ steps.paths.outputs.xz_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.xz_INSTALL }}
key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install
- name: Build xz
if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --free-up-disk --no-tests xz
- name: Save xz to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.xz_SOURCE && ! steps.restore_xz.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.xz_INSTALL }}
key: ${{ steps.paths.outputs.xz_CACHE_KEY }}-install
- name: Build folly
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages build --shared-libs --src-dir=. folly --project-install-prefix folly:/usr/local
- name: Copy artifacts
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages fixup-dyn-deps --strip --src-dir=. folly _artifacts/linux --project-install-prefix folly:/usr/local --final-install-prefix /usr/local
- uses: actions/upload-artifact@v6
with:
name: folly
path: _artifacts
- name: Test folly
run: python3 build/fbcode_builder/getdeps.py --allow-system-packages test --shared-libs --src-dir=. folly --project-install-prefix folly:/usr/local
- name: Show disk space at end
if: always()
run: df -h
================================================
FILE: .github/workflows/getdeps_windows.yml
================================================
# This file was @generated by getdeps.py
name: windows
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
build:
runs-on: windows-2022
steps:
- name: Export boost environment
run: "echo BOOST_ROOT=%BOOST_ROOT_1_83_0% >> %GITHUB_ENV%"
shell: cmd
- name: Fix Git config
run: >
git config --system core.longpaths true &&
git config --system core.autocrlf false &&
git config --system core.symlinks true
shell: cmd
- uses: actions/checkout@v6
- id: paths
name: Query paths
run: python build/fbcode_builder/getdeps.py query-paths --recursive --src-dir=. folly >> $env:GITHUB_OUTPUT
shell: pwsh
- name: Fetch boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests boost
- name: Fetch libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests libsodium
- name: Fetch ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests ninja
- name: Fetch cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests cmake
- name: Fetch double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests double-conversion
- name: Fetch fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests fast_float
- name: Fetch fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests fmt
- name: Fetch gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests gflags
- name: Fetch glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests glog
- name: Fetch googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests googletest
- name: Fetch libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests libdwarf
- name: Fetch lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests lz4
- name: Fetch jom
if: ${{ steps.paths.outputs.jom_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests jom
- name: Fetch perl
if: ${{ steps.paths.outputs.perl_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests perl
- name: Fetch openssl
if: ${{ steps.paths.outputs.openssl_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests openssl
- name: Fetch snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests snappy
- name: Fetch zlib
if: ${{ steps.paths.outputs.zlib_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests zlib
- name: Fetch zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests zstd
- name: Fetch libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
run: python build/fbcode_builder/getdeps.py fetch --no-tests libevent
- name: Restore boost from cache
id: restore_boost
if: ${{ steps.paths.outputs.boost_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Build boost
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests boost
- name: Save boost to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.boost_SOURCE && ! steps.restore_boost.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.boost_INSTALL }}
key: ${{ steps.paths.outputs.boost_CACHE_KEY }}-install
- name: Restore libsodium from cache
id: restore_libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Build libsodium
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libsodium
- name: Save libsodium to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libsodium_SOURCE && ! steps.restore_libsodium.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libsodium_INSTALL }}
key: ${{ steps.paths.outputs.libsodium_CACHE_KEY }}-install
- name: Restore ninja from cache
id: restore_ninja
if: ${{ steps.paths.outputs.ninja_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Build ninja
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests ninja
- name: Save ninja to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.ninja_SOURCE && ! steps.restore_ninja.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.ninja_INSTALL }}
key: ${{ steps.paths.outputs.ninja_CACHE_KEY }}-install
- name: Restore cmake from cache
id: restore_cmake
if: ${{ steps.paths.outputs.cmake_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Build cmake
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests cmake
- name: Save cmake to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.cmake_SOURCE && ! steps.restore_cmake.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.cmake_INSTALL }}
key: ${{ steps.paths.outputs.cmake_CACHE_KEY }}-install
- name: Restore double-conversion from cache
id: restore_double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Build double-conversion
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests double-conversion
- name: Save double-conversion to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.double-conversion_SOURCE && ! steps.restore_double-conversion.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.double-conversion_INSTALL }}
key: ${{ steps.paths.outputs.double-conversion_CACHE_KEY }}-install
- name: Restore fast_float from cache
id: restore_fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Build fast_float
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests fast_float
- name: Save fast_float to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fast_float_SOURCE && ! steps.restore_fast_float.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fast_float_INSTALL }}
key: ${{ steps.paths.outputs.fast_float_CACHE_KEY }}-install
- name: Restore fmt from cache
id: restore_fmt
if: ${{ steps.paths.outputs.fmt_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Build fmt
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests fmt
- name: Save fmt to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.fmt_SOURCE && ! steps.restore_fmt.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.fmt_INSTALL }}
key: ${{ steps.paths.outputs.fmt_CACHE_KEY }}-install
- name: Restore gflags from cache
id: restore_gflags
if: ${{ steps.paths.outputs.gflags_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Build gflags
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests gflags
- name: Save gflags to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.gflags_SOURCE && ! steps.restore_gflags.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.gflags_INSTALL }}
key: ${{ steps.paths.outputs.gflags_CACHE_KEY }}-install
- name: Restore glog from cache
id: restore_glog
if: ${{ steps.paths.outputs.glog_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Build glog
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests glog
- name: Save glog to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.glog_SOURCE && ! steps.restore_glog.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.glog_INSTALL }}
key: ${{ steps.paths.outputs.glog_CACHE_KEY }}-install
- name: Restore googletest from cache
id: restore_googletest
if: ${{ steps.paths.outputs.googletest_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Build googletest
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests googletest
- name: Save googletest to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.googletest_SOURCE && ! steps.restore_googletest.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.googletest_INSTALL }}
key: ${{ steps.paths.outputs.googletest_CACHE_KEY }}-install
- name: Restore libdwarf from cache
id: restore_libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Build libdwarf
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libdwarf
- name: Save libdwarf to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libdwarf_SOURCE && ! steps.restore_libdwarf.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libdwarf_INSTALL }}
key: ${{ steps.paths.outputs.libdwarf_CACHE_KEY }}-install
- name: Restore lz4 from cache
id: restore_lz4
if: ${{ steps.paths.outputs.lz4_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Build lz4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests lz4
- name: Save lz4 to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.lz4_SOURCE && ! steps.restore_lz4.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.lz4_INSTALL }}
key: ${{ steps.paths.outputs.lz4_CACHE_KEY }}-install
- name: Restore jom from cache
id: restore_jom
if: ${{ steps.paths.outputs.jom_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.jom_INSTALL }}
key: ${{ steps.paths.outputs.jom_CACHE_KEY }}-install
- name: Build jom
if: ${{ steps.paths.outputs.jom_SOURCE && ! steps.restore_jom.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests jom
- name: Save jom to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.jom_SOURCE && ! steps.restore_jom.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.jom_INSTALL }}
key: ${{ steps.paths.outputs.jom_CACHE_KEY }}-install
- name: Restore perl from cache
id: restore_perl
if: ${{ steps.paths.outputs.perl_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.perl_INSTALL }}
key: ${{ steps.paths.outputs.perl_CACHE_KEY }}-install
- name: Build perl
if: ${{ steps.paths.outputs.perl_SOURCE && ! steps.restore_perl.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests perl
- name: Save perl to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.perl_SOURCE && ! steps.restore_perl.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.perl_INSTALL }}
key: ${{ steps.paths.outputs.perl_CACHE_KEY }}-install
- name: Restore openssl from cache
id: restore_openssl
if: ${{ steps.paths.outputs.openssl_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.openssl_INSTALL }}
key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install
- name: Build openssl
if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests openssl
- name: Save openssl to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.openssl_SOURCE && ! steps.restore_openssl.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.openssl_INSTALL }}
key: ${{ steps.paths.outputs.openssl_CACHE_KEY }}-install
- name: Restore snappy from cache
id: restore_snappy
if: ${{ steps.paths.outputs.snappy_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Build snappy
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests snappy
- name: Save snappy to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.snappy_SOURCE && ! steps.restore_snappy.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.snappy_INSTALL }}
key: ${{ steps.paths.outputs.snappy_CACHE_KEY }}-install
- name: Restore zlib from cache
id: restore_zlib
if: ${{ steps.paths.outputs.zlib_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.zlib_INSTALL }}
key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install
- name: Build zlib
if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests zlib
- name: Save zlib to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.zlib_SOURCE && ! steps.restore_zlib.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.zlib_INSTALL }}
key: ${{ steps.paths.outputs.zlib_CACHE_KEY }}-install
- name: Restore zstd from cache
id: restore_zstd
if: ${{ steps.paths.outputs.zstd_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Build zstd
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests zstd
- name: Save zstd to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.zstd_SOURCE && ! steps.restore_zstd.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.zstd_INSTALL }}
key: ${{ steps.paths.outputs.zstd_CACHE_KEY }}-install
- name: Restore libevent from cache
id: restore_libevent
if: ${{ steps.paths.outputs.libevent_SOURCE }}
uses: actions/cache/restore@v4
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Build libevent
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
run: python build/fbcode_builder/getdeps.py build --free-up-disk --no-tests libevent
- name: Save libevent to cache
uses: actions/cache/save@v4
if: ${{ steps.paths.outputs.libevent_SOURCE && ! steps.restore_libevent.outputs.cache-hit }}
with:
path: ${{ steps.paths.outputs.libevent_INSTALL }}
key: ${{ steps.paths.outputs.libevent_CACHE_KEY }}-install
- name: Build folly
run: python build/fbcode_builder/getdeps.py build --src-dir=. folly
- name: Copy artifacts
run: python build/fbcode_builder/getdeps.py fixup-dyn-deps --src-dir=. folly _artifacts/windows --final-install-prefix /usr/local
- uses: actions/upload-artifact@v6
with:
name: folly
path: _artifacts
- name: Test folly
run: python build/fbcode_builder/getdeps.py test --src-dir=. folly
================================================
FILE: .github/workflows/oss-build-and-test.yml
================================================
name: Buck build and test
on: [push, pull_request, workflow_dispatch]
jobs:
get-toolchains-to-install:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- uses: facebook/install-dotslash@latest
- name: get_buck_graph
run: |
BUCK_GRAPH=$(./buck2 cquery ... --output-attribute '^buck.type$|^name$')
echo "$BUCK_GRAPH" > buck_graph_results.json
shell: bash
- name: Check if rust_binary
id: check_rust
run: |
OUTPUT=$(cat buck_graph_results.json)
if [[ "$OUTPUT" == *"rust_binary"* ]]; then
echo "uses_rust=true" >> $GITHUB_ENV
fi
shell: bash
- name: Check if cxx_binary
id: check_cxx
run: |
OUTPUT=$(cat buck_graph_results.json)
if [[ "$OUTPUT" == *"cxx_binary"* ]]; then
echo "uses_cxx=true" >> $GITHUB_ENV
fi
shell: bash
- name: Check if ocaml_binary
id: check_ocaml
run: |
OUTPUT=$(cat buck_graph_results.json)
if [[ "$OUTPUT" == *"ocaml_binary"* ]]; then
echo "uses_ocaml=true" >> $GITHUB_ENV
fi
shell: bash
- name: Check if python_binary
id: check_python
run: |
OUTPUT=$(cat buck_graph_results.json)
if [[ "$OUTPUT" == *"python_binary"* ]]; then
echo "uses_python=true" >> $GITHUB_ENV
fi
shell: bash
outputs:
uses_rust: ${{ env.uses_rust }}
uses_cxx: ${{ env.uses_cxx }}
uses_ocaml: ${{env.uses_ocaml}}
uses_python: ${{env.uses_python}}
ubuntu-os-buck-build-and-test:
needs: get-toolchains-to-install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- run: sudo apt-get update
shell: bash
- uses: facebook/install-dotslash@latest
- name: Install Rust toolchain
if: needs.get-toolchains-to-install.outputs.uses_rust == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Install C++ toolchain
if: needs.get-toolchains-to-install.outputs.uses_cxx == 'true'
run: |
sudo apt-get install cmake llvm cppcheck python3-pip
sudo pip3 install conan==1.*
shell: bash
- name: Install OCaml toolchain
if: needs.get-toolchains-to-install.outputs.uses_ocaml == 'true'
uses: ocaml/setup-ocaml@v2
with:
ocaml-compiler: "5.1"
- name: Install Python toolchain
if: needs.get-toolchains-to-install.outputs.uses_python == 'true'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: buck2 build and test
run: bash ./.github/scripts/buck_build_and_test.sh
windows-os-buck-build-and-test:
needs: get-toolchains-to-install
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- uses: facebook/install-dotslash@latest
- name: Install Rust toolchain
if: needs.get-toolchains-to-install.outputs.uses_rust == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Install C++ toolchain
if: needs.get-toolchains-to-install.outputs.uses_cxx == 'true'
run: |
choco install llvm cmake conan cppcheck -y
if ($LASTEXITCODE -eq 3010) { $LASTEXITCODE = 0 }
shell: pwsh
- name: Install OCaml toolchain
if: needs.get-toolchains-to-install.outputs.uses_ocaml == 'true'
uses: ocaml/setup-ocaml@v2
with:
ocaml-compiler: "4.12.0"
- name: Install Python toolchain
if: needs.get-toolchains-to-install.outputs.uses_python == 'true'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: buck2 build and test
run: bash ./.github/scripts/buck_build_and_test.sh
mac-os-buck-build-and-test:
needs: get-toolchains-to-install
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: 'true'
- uses: facebook/install-dotslash@latest
- name: Install Rust toolchain
if: needs.get-toolchains-to-install.outputs.uses_rust == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Install C++ toolchain
if: needs.get-toolchains-to-install.outputs.uses_cxx == 'true'
run: |
brew install cmake llvm cppcheck python3 conan@1
shell: bash
- name: Install OCaml toolchain
if: needs.get-toolchains-to-install.outputs.uses_ocaml == 'true'
uses: ocaml/setup-ocaml@v2
with:
ocaml-compiler: "5.1"
- name: Install Python toolchain
if: needs.get-toolchains-to-install.outputs.uses_python == 'true'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install homebrew deps
run: |
BUCK_GRAPH=$(./buck2 cquery "attrregexfilter(labels, 'third-party:homebrew:', deps(//...))" --json --output-attribute=labels)
HOMEBREW_PACKAGES=$(echo $BUCK_GRAPH | jq '[.[] | .labels] | flatten | unique | map(select(contains("third-party:homebrew:")) | sub("third-party:homebrew:"; "")) | .[] | @text')
echo $HOMEBREW_PACKAGES
echo $HOMEBREW_PACKAGES | xargs brew install pkg-config
- name: buck2 build and test
run: bash ./.github/scripts/buck_build_and_test.sh
================================================
FILE: .gitignore
================================================
*.o
*.lo
*.la
.dirstamp
Makefile
Makefile.in
.libs
.deps
stamp-h1
folly-config.h
_configs.sed
aclocal.m4
autom4te.cache
build-aux
libtool
folly/test/gtest
folly/folly-config.h
folly/**/test/*_benchmark
folly/**/test/*.log
folly/**/test/*_test
folly/**/test/*_test_using_jemalloc
folly/**/test/*.trs
folly/config.*
folly/configure
folly/logging/example/logging_example
folly/libfolly.pc
folly/m4/libtool.m4
folly/m4/ltoptions.m4
folly/m4/ltsugar.m4
folly/m4/ltversion.m4
folly/m4/lt~obsolete.m4
folly/generate_fingerprint_tables
folly/FingerprintTables.cpp
_build
# Ignore all files generated by Buck2
buck-out/
================================================
FILE: .projectid
================================================
folly
================================================
FILE: BUCK
================================================
load("@fbcode_macros//build_defs:native_rules.bzl", "buck_genrule")
oncall("fbcode_entropy_wardens_folly")
buck_genrule(
name = "folly-config.h",
srcs = {file: file for file in glob([
"CMake/*",
"build/fbcode_builder/CMake/*",
])} |
{"CMakeLists.txt": "CMakeListsForBuck2.txt"},
out = "folly-config.h",
cmd = "cmake . && mv folly/folly-config.h $OUT",
default_target_platform = "prelude//platforms:default",
labels = [
"third-party:fedora:cmake",
"third-party:homebrew:cmake",
"third-party:ubuntu:cmake",
],
remote = False,
)
================================================
FILE: CMake/FindCython.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Find Cython
#
# This module sets the following variables:
# - Cython_FOUND
# - CYTHON_EXE
# - CYTHON_VERSION_STRING
#
find_program(CYTHON_EXE
NAMES cython cython3)
if (CYTHON_EXE)
execute_process(COMMAND ${CYTHON_EXE} --version
RESULT_VARIABLE _cython_retcode
OUTPUT_VARIABLE _cython_output
ERROR_VARIABLE _cython_output
OUTPUT_STRIP_TRAILING_WHITESPACE)
if (${_cython_retcode} EQUAL 0)
separate_arguments(_cython_output)
list(GET _cython_output -1 CYTHON_VERSION_STRING)
message(STATUS "Found Cython Version ${CYTHON_VERSION_STRING}")
else ()
message(STATUS "Failed to get Cython version")
endif ()
else ()
message(STATUS "Cython not found")
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
Cython
REQUIRED_VARS CYTHON_EXE CYTHON_VERSION_STRING
VERSION_VAR CYTHON_VERSION_STRING
)
================================================
FILE: CMake/FindFastFloat.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# - Try to find fast_float library
# This will define
# FASTFLOAT_FOUND
# FASTFLOAT_INCLUDE_DIR
#
find_path(FASTFLOAT_INCLUDE_DIR NAMES fast_float/fast_float.h)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
FastFloat DEFAULT_MSG
FASTFLOAT_INCLUDE_DIR
)
mark_as_advanced(FASTFLOAT_INCLUDE_DIR)
================================================
FILE: CMake/FindFmt.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
find_path(LIBFMT_INCLUDE_DIR fmt/core.h)
mark_as_advanced(LIBFMT_INCLUDE_DIR)
find_library(LIBFMT_LIBRARY NAMES fmt fmtd)
mark_as_advanced(LIBFMT_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LIBFMT
DEFAULT_MSG
LIBFMT_LIBRARY LIBFMT_INCLUDE_DIR)
if(LIBFMT_FOUND)
set(LIBFMT_LIBRARIES ${LIBFMT_LIBRARY})
set(LIBFMT_INCLUDE_DIRS ${LIBFMT_INCLUDE_DIR})
message(STATUS "Found {fmt}: ${LIBFMT_LIBRARY}")
add_library(fmt::fmt UNKNOWN IMPORTED)
set_target_properties(
fmt::fmt PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${LIBFMT_INCLUDE_DIR}"
)
set_target_properties(
fmt::fmt PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${LIBFMT_LIBRARY}"
)
endif()
================================================
FILE: CMake/FindLZ4.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Finds liblz4.
#
# This module defines:
# LZ4_FOUND
# LZ4_INCLUDE_DIR
# LZ4_LIBRARY
#
find_path(LZ4_INCLUDE_DIR NAMES lz4.h)
find_library(LZ4_LIBRARY_DEBUG NAMES lz4d)
find_library(LZ4_LIBRARY_RELEASE NAMES lz4)
include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(LZ4)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LZ4 DEFAULT_MSG
LZ4_LIBRARY LZ4_INCLUDE_DIR
)
if (LZ4_FOUND)
message(STATUS "Found LZ4: ${LZ4_LIBRARY}")
endif()
mark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY)
================================================
FILE: CMake/FindLibAIO.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
find_path(LIBAIO_INCLUDE_DIR NAMES libaio.h)
mark_as_advanced(LIBAIO_INCLUDE_DIR)
find_library(LIBAIO_LIBRARY NAMES aio)
mark_as_advanced(LIBAIO_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LIBAIO
REQUIRED_VARS LIBAIO_LIBRARY LIBAIO_INCLUDE_DIR)
if(LIBAIO_FOUND)
set(LIBAIO_LIBRARIES ${LIBAIO_LIBRARY})
set(LIBAIO_INCLUDE_DIRS ${LIBAIO_INCLUDE_DIR})
endif()
================================================
FILE: CMake/FindLibDwarf.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# dwarf.h is typically installed in a libdwarf/ subdirectory on Debian-style
# Linux distributions. It is not installed in a libdwarf/ subdirectory on Mac
# systems when installed with Homebrew. Newer homebrew installations install
# it in libdwarf-0. Search for it in all locations.
find_path(LIBDWARF_INCLUDE_DIR NAMES dwarf.h PATH_SUFFIXES libdwarf libdwarf-0)
mark_as_advanced(LIBDWARF_INCLUDE_DIR)
find_library(LIBDWARF_LIBRARY NAMES dwarf)
mark_as_advanced(LIBDWARF_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LIBDWARF
REQUIRED_VARS LIBDWARF_LIBRARY LIBDWARF_INCLUDE_DIR)
if(LIBDWARF_FOUND)
set(LIBDWARF_LIBRARIES ${LIBDWARF_LIBRARY})
set(LIBDWARF_INCLUDE_DIRS ${LIBDWARF_INCLUDE_DIR})
endif()
================================================
FILE: CMake/FindLibUring.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
find_path(LIBURING_INCLUDE_DIR NAMES liburing.h)
mark_as_advanced(LIBURING_INCLUDE_DIR)
find_library(LIBURING_LIBRARY NAMES uring)
mark_as_advanced(LIBURING_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LIBURING
REQUIRED_VARS LIBURING_LIBRARY LIBURING_INCLUDE_DIR)
if(LIBURING_FOUND)
set(LIBURING_LIBRARIES ${LIBURING_LIBRARY})
set(LIBURING_INCLUDE_DIRS ${LIBURING_INCLUDE_DIR})
endif()
================================================
FILE: CMake/FindLibiberty.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
find_path(LIBIBERTY_INCLUDE_DIR NAMES libiberty.h PATH_SUFFIXES libiberty)
mark_as_advanced(LIBIBERTY_INCLUDE_DIR)
find_library(LIBIBERTY_LIBRARY NAMES iberty)
mark_as_advanced(LIBIBERTY_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LIBIBERTY
REQUIRED_VARS LIBIBERTY_LIBRARY LIBIBERTY_INCLUDE_DIR)
if(LIBIBERTY_FOUND)
set(LIBIBERTY_LIBRARIES ${LIBIBERTY_LIBRARY})
set(LIBIBERTY_INCLUDE_DIRS ${LIBIBERTY_INCLUDE_DIR})
endif()
================================================
FILE: CMake/FindLibsodium.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
find_path(LIBSODIUM_INCLUDE_DIR NAMES sodium.h)
mark_as_advanced(LIBSODIUM_INCLUDE_DIR)
find_library(LIBSODIUM_LIBRARY NAMES sodium)
mark_as_advanced(LIBSODIUM_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LIBSODIUM
REQUIRED_VARS LIBSODIUM_LIBRARY LIBSODIUM_INCLUDE_DIR)
if(LIBSODIUM_FOUND)
set(LIBSODIUM_LIBRARIES ${LIBSODIUM_LIBRARY})
set(LIBSODIUM_INCLUDE_DIRS ${LIBSODIUM_INCLUDE_DIR})
message(STATUS "Found Libsodium: ${LIBSODIUM_LIBRARY}")
endif()
================================================
FILE: CMake/FindSnappy.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Find the Snappy libraries
#
# This module defines:
# SNAPPY_FOUND
# SNAPPY_INCLUDE_DIR
# SNAPPY_LIBRARY
find_path(SNAPPY_INCLUDE_DIR NAMES snappy.h)
find_library(SNAPPY_LIBRARY_DEBUG NAMES snappyd)
find_library(SNAPPY_LIBRARY_RELEASE NAMES snappy)
include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(SNAPPY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
SNAPPY DEFAULT_MSG
SNAPPY_LIBRARY SNAPPY_INCLUDE_DIR
)
mark_as_advanced(SNAPPY_INCLUDE_DIR SNAPPY_LIBRARY)
================================================
FILE: CMake/FindZstd.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# - Try to find Facebook zstd library
# This will define
# ZSTD_FOUND
# ZSTD_INCLUDE_DIR
# ZSTD_LIBRARY
#
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)
include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(ZSTD)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
ZSTD DEFAULT_MSG
ZSTD_LIBRARY ZSTD_INCLUDE_DIR
)
if (ZSTD_FOUND)
message(STATUS "Found Zstd: ${ZSTD_LIBRARY}")
endif()
mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)
================================================
FILE: CMake/FollyCompilerMSVC.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Some additional configuration options.
option(MSVC_ENABLE_ALL_WARNINGS "If enabled, pass /Wall to the compiler." ON)
option(MSVC_ENABLE_DEBUG_INLINING "If enabled, enable inlining in the debug configuration. This allows /Zc:inline to be far more effective." OFF)
option(MSVC_ENABLE_FAST_LINK "If enabled, pass /DEBUG:FASTLINK to the linker. This makes linking faster, but the gtest integration for Visual Studio can't currently handle the .pdbs generated." OFF)
option(MSVC_ENABLE_LEAN_AND_MEAN_WINDOWS "If enabled, define WIN32_LEAN_AND_MEAN to include a smaller subset of Windows.h" ON)
option(MSVC_ENABLE_LTCG "If enabled, use Link Time Code Generation for Release builds." OFF)
option(MSVC_ENABLE_PARALLEL_BUILD "If enabled, build multiple source files in parallel." ON)
option(MSVC_ENABLE_STATIC_ANALYSIS "If enabled, do more complex static analysis and generate warnings appropriately." OFF)
option(MSVC_USE_STATIC_RUNTIME "If enabled, build against the static, rather than the dynamic, runtime." OFF)
option(MSVC_SUPPRESS_BOOST_CONFIG_OUTDATED "If enabled, suppress Boost's warnings about the config being out of date." ON)
# Alas, option() doesn't support string values.
set(MSVC_FAVORED_ARCHITECTURE "blend" CACHE STRING "One of 'blend', 'AMD64', 'INTEL64', or 'ATOM'. This tells the compiler to generate code optimized to run best on the specified architecture.")
# Add a pretty drop-down selector for these values when using the GUI.
set_property(
CACHE MSVC_FAVORED_ARCHITECTURE
PROPERTY STRINGS
blend
AMD64
ATOM
INTEL64
)
# Validate, and then add the favored architecture.
if (NOT MSVC_FAVORED_ARCHITECTURE STREQUAL "blend" AND NOT MSVC_FAVORED_ARCHITECTURE STREQUAL "AMD64" AND NOT MSVC_FAVORED_ARCHITECTURE STREQUAL "INTEL64" AND NOT MSVC_FAVORED_ARCHITECTURE STREQUAL "ATOM")
message(FATAL_ERROR "MSVC_FAVORED_ARCHITECTURE must be set to one of exactly, 'blend', 'AMD64', 'INTEL64', or 'ATOM'! Got '${MSVC_FAVORED_ARCHITECTURE}' instead!")
endif()
set(MSVC_LANGUAGE_VERSION "c++20" CACHE STRING "One of 'c++20', or 'c++latest'. This determines which version of C++ to compile as.")
set_property(
CACHE MSVC_LANGUAGE_VERSION
PROPERTY STRINGS
"c++20"
"c++latest"
)
############################################################
# We need to adjust a couple of the default option sets.
############################################################
# If the static runtime is requested, we have to
# overwrite some of CMake's defaults.
if (MSVC_USE_STATIC_RUNTIME)
foreach(flag_var
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if (${flag_var} MATCHES "/MD")
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endif()
endforeach()
endif()
# The Ninja generator doesn't de-dup the exception mode flag, so remove the
# default flag so that MSVC doesn't warn about it on every single file.
if ("${CMAKE_GENERATOR}" STREQUAL "Ninja")
foreach(flag_var
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if (${flag_var} MATCHES "/EHsc")
string(REGEX REPLACE "/EHsc" "" ${flag_var} "${${flag_var}}")
endif()
endforeach()
endif()
# In order for /Zc:inline, which speeds up the build significantly, to work
# we need to remove the /Ob0 parameter that CMake adds by default, because that
# would normally disable all inlining.
foreach(flag_var CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG)
if (${flag_var} MATCHES "/Ob0")
string(REGEX REPLACE "/Ob0" "" ${flag_var} "${${flag_var}}")
endif()
endforeach()
# When building with Ninja, or with /MP enabled, there is the potential
# for multiple processes to need to lock the same pdb file.
# The /FS option (which is implicitly enabled by /MP) is widely believed
# to be the solution for this, but even with /FS enabled the problem can
# still randomly occur.
# https://stackoverflow.com/a/58020501/149111 suggests that /Z7 should be
# used; rather than placing the debug info into a .pdb file it embeds it
# into the object files in a similar way to gcc/clang which should reduce
# contention and potentially make the build faster... but at the cost of
# larger object files
foreach(flag_var CMAKE_C_FLAGS_DEBUG CMAKE_CXX_FLAGS_DEBUG)
if (${flag_var} MATCHES "/Zi")
string(REGEX REPLACE "/Zi" "/Z7" ${flag_var} "${${flag_var}}")
endif()
endforeach()
# Apply the option set for Folly to the specified target.
function(apply_folly_compile_options_to_target THETARGET)
# The general options passed:
target_compile_options(${THETARGET}
PUBLIC
/EHs # Don't catch structured exceptions with catch (...)
/GF # There are bugs with constexpr StringPiece when string pooling is disabled.
/Zc:referenceBinding # Disallow temporaries from binding to non-const lvalue references.
/Zc:rvalueCast # Enforce the standard rules for explicit type conversion.
/Zc:implicitNoexcept # Enable implicit noexcept specifications where required, such as destructors.
/Zc:strictStrings # Don't allow conversion from a string literal to mutable characters.
/Zc:threadSafeInit # Enable thread-safe function-local statics initialization.
/Zc:throwingNew # Assume operator new throws on failure.
/permissive- # Be mean, don't allow bad non-standard stuff (C++/CLI, __declspec, etc. are all left intact).
/std:${MSVC_LANGUAGE_VERSION} # Build in the requested version of C++
/utf-8 # fmt needs unicode support, which requires compiling with /utf-8
PRIVATE
/bigobj # Support objects with > 65k sections. Needed due to templates.
/favor:${MSVC_FAVORED_ARCHITECTURE} # Architecture to prefer when generating code.
/Zc:inline # Have the compiler eliminate unreferenced COMDAT functions and data before emitting the object file.
$<$:/Wall> # Enable all warnings if requested.
$<$:/MP> # Enable multi-processor compilation if requested.
$<$:/analyze> # Enable static analysis if requested.
# Debug builds
$<$:
/Gy- # Disable function level linking.
$<$:/Ob2> # Add /Ob2 if allowing inlining in debug mode.
>
# Non-debug builds
$<$>:
/Gw # Optimize global data. (-fdata-sections)
/Gy # Enable function level linking. (-ffunction-sections)
/Qpar # Enable parallel code generation.
/Oi # Enable intrinsic functions.
/Ot # Favor fast code.
$<$:/GL> # Enable link time code generation.
>
)
target_compile_options(${THETARGET}
PUBLIC
/wd4191 # 'type cast' unsafe conversion of function pointers
/wd4291 # no matching operator delete found
/wd4309 # '=' truncation of constant value
/wd4310 # cast truncates constant value
/wd4366 # result of unary '&' operator may be unaligned
/wd4587 # behavior change; constructor no longer implicitly called
/wd4592 # symbol will be dynamically initialized (implementation limitation)
/wd4628 # digraphs not supported with -Ze
/wd4723 # potential divide by 0
/wd4724 # potential mod by 0
/wd4868 # compiler may not enforce left-to-right evaluation order
/wd4996 # user deprecated
# The warnings that are disabled:
/wd4068 # Unknown pragma.
/wd4091 # 'typedef' ignored on left of '' when no variable is declared.
/wd4146 # Unary minus applied to unsigned type, result still unsigned.
/wd4800 # Values being forced to bool, this happens many places, and is a "performance warning".
# NOTE: glog/logging.h:1116 change to `size_t pcount() const { return size_t(pptr() - pbase()); }`
# NOTE: gmock/gmock-spec-builders.h:1177 change to `*static_cast*>(untyped_actions_[size_t(count - 1)]) :`
# NOTE: gmock/gmock-spec-builders.h:1749 change to `const size_t count = untyped_expectations_.size();`
# NOTE: gmock/gmock-spec-builders.h:1754 change to `for (size_t i = 0; i < count; i++) {`
# NOTE: gtest/gtest-printers.h:173 change to `const internal::BiggestInt kBigInt = internal::BiggestInt(value);`
# NOTE: gtest/internal/gtest-internal.h:890 add `GTEST_DISABLE_MSC_WARNINGS_PUSH_(4365)`
# NOTE: gtest/internal/gtest-internal.h:894 ass `GTEST_DISABLE_MSC_WARNINGS_POP_()`
# NOTE: boost/crc.hpp:578 change to `{ return static_cast(x ^ rem); }`
# NOTE: boost/regex/v4/match_results.hpp:126 change to `return m_subs[size_type(sub)].length();`
# NOTE: boost/regex/v4/match_results.hpp:226 change to `return m_subs[size_type(sub)];`
# NOTE: boost/date_time/adjust_functors.hpp:67 change to `origDayOfMonth_ = short(ymd.day);`
# NOTE: boost/date_time/adjust_functors.hpp:75 change to `wrap_int2 wi(short(ymd.month));`
# NOTE: boost/date_time/adjust_functors.hpp:82 change to `day_type resultingEndOfMonthDay(cal_type::end_of_month_day(static_cast(year), static_cast(wi.as_int())));`
# NOTE: boost/date_time/adjust_functors.hpp:85 change to `return date_type(static_cast(year), static_cast(wi.as_int()), resultingEndOfMonthDay) - d;`
# NOTE: boost/date_time/adjust_functors.hpp:87 change to `day_type dayOfMonth = static_cast(origDayOfMonth_);`
# NOTE: boost/date_time/adjust_functors.hpp:91 change to `return date_type(static_cast(year), static_cast(wi.as_int()), dayOfMonth) - d;`
# NOTE: boost/date_time/adjust_functors.hpp:98 change to `origDayOfMonth_ = short(ymd.day);`
# NOTE: boost/date_time/adjust_functors.hpp:106 change to `wrap_int2 wi(short(ymd.month));`
# NOTE: boost/date_time/adjust_functors.hpp:111 change to `day_type resultingEndOfMonthDay(cal_type::end_of_month_day(static_cast(year), static_cast(wi.as_int())));`
# NOTE: boost/date_time/adjust_functors.hpp:114 change to `return date_type(static_cast(year), static_cast(wi.as_int()), resultingEndOfMonthDay) - d;`
# NOTE: boost/date_time/adjust_functors.hpp:116 change to `day_type dayOfMonth = static_cast(origDayOfMonth_);`
# NOTE: boost/date_time/adjust_functors.hpp:120 change to `return date_type(static_cast(year), static_cast(wi.as_int()), dayOfMonth) - d;`
# NOTE: boost/date_time/gregorian_calendar.ipp:81 change to `unsigned long d = static_cast(ymd.day + ((153*m + 2)/5) + 365*y + (y/4) - (y/100) + (y/400) - 32045);`
# NOTE: boost/date_time/gregorian/greg_date.hpp:122 change to `unsigned short eom_day = gregorian_calendar::end_of_month_day(ymd.year, ymd.month);`
# NOTE: boost/thread/future.hpp:1050 change to `locks[std::ptrdiff_t(i)]=BOOST_THREAD_MAKE_RV_REF(boost::unique_lock(futures[i].future_->mutex));`
# NOTE: boost/thread/future.hpp:1063 change to `locks[std::ptrdiff_t(i)].unlock();`
# NOTE: boost/thread/win32/basic_recursive_mutex.hpp:47 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`
# NOTE: boost/thread/win32/basic_recursive_mutex.hpp:53 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`
# NOTE: boost/thread/win32/basic_recursive_mutex.hpp:64 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`
# NOTE: boost/thread/win32/basic_recursive_mutex.hpp:78 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`
# NOTE: boost/thread/win32/basic_recursive_mutex.hpp:84 change to `long const current_thread_id=long(win32::GetCurrentThreadId());`
# NOTE: boost/thread/win32/condition_variable.hpp:79 change to `detail::win32::ReleaseSemaphore(semaphore,long(count_to_release),0);`
# NOTE: boost/thread/win32/condition_variable.hpp:84 change to `release(unsigned(detail::interlocked_read_acquire(&waiters)));`
# NOTE: boost/algorithm/string/detail/classification.hpp:85 change to `std::size_t Size=std::size_t(::boost::distance(Range));`
/wd4018 # Signed/unsigned mismatch.
/wd4365 # Signed/unsigned mismatch.
/wd4388 # Signed/unsigned mismatch on relative comparison operator.
/wd4389 # Signed/unsigned mismatch on equality comparison operator.
# TODO:
/wd4100 # Unreferenced formal parameter.
/wd4459 # Declaration of parameter hides global declaration.
/wd4505 # Unreferenced local function has been removed.
/wd4701 # Potentially uninitialized local variable used.
/wd4702 # Unreachable code.
# These warnings are disabled because we've
# enabled all warnings. If all warnings are
# not enabled, we still need to disable them
# for consuming libs.
/wd4061 # Enum value not handled by a case in a switch on an enum. This isn't very helpful because it is produced even if a default statement is present.
/wd4127 # Conditional expression is constant.
/wd4200 # Non-standard extension, zero sized array.
/wd4201 # Non-standard extension used: nameless struct/union.
/wd4296 # '<' Expression is always false.
/wd4316 # Object allocated on the heap may not be aligned to 128.
/wd4324 # Structure was padded due to alignment specifier.
/wd4355 # 'this' used in base member initializer list.
/wd4371 # Layout of class may have changed due to fixes in packing.
/wd4435 # Object layout under /vd2 will change due to virtual base.
/wd4514 # Unreferenced inline function has been removed. (caused by /Zc:inline)
/wd4548 # Expression before comma has no effect. I wouldn't disable this normally, but malloc.h triggers this warning.
/wd4571 # Semantics of catch(...) changed in VC 7.1
/wd4574 # ifdef'd macro was defined to 0.
/wd4582 # Constructor is not implicitly called.
/wd4583 # Destructor is not implicitly called.
/wd4619 # Invalid warning number used in #pragma warning.
/wd4623 # Default constructor was implicitly defined as deleted.
/wd4625 # Copy constructor was implicitly defined as deleted.
/wd4626 # Assignment operator was implicitly defined as deleted.
/wd4643 # Forward declaring standard library types is not permitted.
/wd4647 # Behavior change in __is_pod.
/wd4668 # Macro was not defined, replacing with 0.
/wd4706 # Assignment within conditional expression.
/wd4710 # Function was not inlined.
/wd4711 # Function was selected for automated inlining.
/wd4714 # Function marked as __forceinline not inlined.
/wd4820 # Padding added after data member.
/wd5026 # Move constructor was implicitly defined as deleted.
/wd5027 # Move assignment operator was implicitly defined as deleted.
/wd5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file. This is needed because of how boost does things.
/wd5045 # Compiler will insert Spectre mitigation for memory load if /Qspectre switch is specified.
# Warnings to treat as errors:
/we4099 # Mixed use of struct and class on same type names.
/we4129 # Unknown escape sequence. This is usually caused by incorrect escaping.
/we4566 # Character cannot be represented in current charset. This is remidied by prefixing string with "u8".
PRIVATE
# Warnings disabled for /analyze
$<$:
/wd6001 # Using uninitialized memory. This is disabled because it is wrong 99% of the time.
/wd6011 # Dereferencing potentially NULL pointer.
/wd6031 # Return value ignored.
/wd6235 # ( || ) is always a non-zero constant.
/wd6237 # ( && ) is always zero. is never evaluated and may have side effects.
/wd6239 # ( && ) always evaluates to the result of .
/wd6240 # ( && ) always evaluates to the result of .
/wd6246 # Local declaration hides declaration of same name in outer scope.
/wd6248 # Setting a SECURITY_DESCRIPTOR's DACL to NULL will result in an unprotected object. This is done by one of the boost headers.
/wd6255 # _alloca indicates failure by raising a stack overflow exception.
/wd6262 # Function uses more than x bytes of stack space.
/wd6271 # Extra parameter passed to format function. The analysis pass doesn't recognize %j or %z, even though the runtime does.
/wd6285 # ( || ) is always true.
/wd6297 # 32-bit value is shifted then cast to 64-bits. The places this occurs never use more than 32 bits.
/wd6308 # Realloc might return null pointer: assigning null pointer to '', which is passed as an argument to 'realloc', will cause the original memory to leak.
/wd6326 # Potential comparison of a constant with another constant.
/wd6330 # Unsigned/signed mismatch when passed as a parameter.
/wd6340 # Mismatch on sign when passed as format string value.
/wd6387 # '' could be '0': This does not adhere to the specification for a function.
/wd28182 # Dereferencing NULL pointer. '' contains the same NULL value as ''.
/wd28251 # Inconsistent annotation for function. This is because we only annotate the declaration and not the definition.
/wd28278 # Function appears with no prototype in scope.
>
)
# And the extra defines:
target_compile_definitions(${THETARGET}
PUBLIC
_CRT_NONSTDC_NO_WARNINGS # Don't deprecate posix names of functions.
_CRT_SECURE_NO_WARNINGS # Don't deprecate the non _s versions of various standard library functions, because safety is for chumps.
_SCL_SECURE_NO_WARNINGS # Don't deprecate the non _s versions of various standard library functions, because safety is for chumps.
_STL_EXTRA_DISABLED_WARNINGS=4774\ 4987
$<$:_HAS_AUTO_PTR_ETC=1> # We're building in C++ 17 or greater mode, but certain dependencies (Boost) still have dependencies on unary_function and binary_function, so we have to make sure not to remove them.
$<$:WIN32_LEAN_AND_MEAN> # Don't include most of Windows.h
$<$:BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE> # MSVC moves faster than boost, so add a quick way to disable the messages.
)
# Ignore a warning about an object file not defining any symbols,
# these are known, and we don't care.
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY STATIC_LIBRARY_FLAGS " /ignore:4221")
# The options to pass to the linker:
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /INCREMENTAL") # Do incremental linking.
if (NOT $ STREQUAL "STATIC_LIBRARY")
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /OPT:NOREF") # No unreferenced data elimination.
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /OPT:NOICF") # No Identical COMDAT folding.
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /OPT:REF") # Remove unreferenced functions and data.
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /OPT:ICF") # Identical COMDAT folding.
endif()
if (MSVC_ENABLE_FAST_LINK)
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /DEBUG:FASTLINK") # Generate a partial PDB file that simply references the original object and library files.
endif()
# Add /GL to the compiler, and /LTCG to the linker
# if link time code generation is enabled.
if (MSVC_ENABLE_LTCG)
set_property(TARGET ${THETARGET} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /LTCG")
endif()
endfunction()
list(APPEND FOLLY_LINK_LIBRARIES Iphlpapi.lib Ws2_32.lib)
================================================
FILE: CMake/FollyCompilerUnix.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(CMAKE_CXX_FLAGS_COMMON "-g -Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_COMMON}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_COMMON} -O3")
list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE")
function(apply_folly_compile_options_to_target THETARGET)
target_compile_definitions(${THETARGET}
PRIVATE
_REENTRANT
_GNU_SOURCE
)
target_compile_options(${THETARGET}
PRIVATE
-g
-finput-charset=UTF-8
-fsigned-char
-Wall
-Wno-deprecated
-Wno-deprecated-declarations
-Wno-sign-compare
-Wno-unused
-Wuninitialized
-Wunused-label
-Wunused-result
${FOLLY_CXX_FLAGS}
)
endfunction()
================================================
FILE: CMake/FollyConfigChecks.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include(CheckCXXSourceCompiles)
include(CheckCXXSourceRuns)
include(CheckFunctionExists)
include(CheckIncludeFileCXX)
include(CheckSymbolExists)
include(CheckTypeSize)
include(CheckCXXCompilerFlag)
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND FOLLY_CXX_FLAGS -Wno-psabi)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
CHECK_INCLUDE_FILE_CXX(malloc_np.h FOLLY_USE_JEMALLOC)
else()
CHECK_INCLUDE_FILE_CXX(jemalloc/jemalloc.h FOLLY_USE_JEMALLOC)
endif()
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows")
# clang only rejects unknown warning flags if -Werror=unknown-warning-option
# is also specified.
check_cxx_compiler_flag(
-Werror=unknown-warning-option
COMPILER_HAS_UNKNOWN_WARNING_OPTION)
if (COMPILER_HAS_UNKNOWN_WARNING_OPTION)
set(CMAKE_REQUIRED_FLAGS
"${CMAKE_REQUIRED_FLAGS} -Werror=unknown-warning-option")
endif()
check_cxx_compiler_flag(-Wshadow-local COMPILER_HAS_W_SHADOW_LOCAL)
check_cxx_compiler_flag(
-Wshadow-compatible-local
COMPILER_HAS_W_SHADOW_COMPATIBLE_LOCAL)
if (COMPILER_HAS_W_SHADOW_LOCAL AND COMPILER_HAS_W_SHADOW_COMPATIBLE_LOCAL)
set(FOLLY_HAVE_SHADOW_LOCAL_WARNINGS ON)
list(APPEND FOLLY_CXX_FLAGS -Wshadow-compatible-local)
endif()
check_cxx_compiler_flag(
-Wnullability-completeness
COMPILER_HAS_W_NULLABILITY_COMPLETENESS)
if (COMPILER_HAS_W_NULLABILITY_COMPLETENESS)
list(APPEND FOLLY_CXX_FLAGS -Wno-nullability-completeness)
endif()
check_cxx_compiler_flag(
-Winconsistent-missing-override
COMPILER_HAS_W_INCONSISTENT_MISSING_OVERRIDE)
if (COMPILER_HAS_W_INCONSISTENT_MISSING_OVERRIDE)
list(APPEND FOLLY_CXX_FLAGS -Wno-inconsistent-missing-override)
endif()
check_cxx_compiler_flag(-fopenmp COMPILER_HAS_F_OPENMP)
if (COMPILER_HAS_F_OPENMP)
list(APPEND FOLLY_CXX_FLAGS -fopenmp)
endif()
endif()
set(FOLLY_ORIGINAL_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}")
string(REGEX REPLACE
"-std=(c|gnu)\\+\\+.."
""
CMAKE_REQUIRED_FLAGS
"${CMAKE_REQUIRED_FLAGS}")
check_symbol_exists(pthread_atfork pthread.h FOLLY_HAVE_PTHREAD_ATFORK)
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE)
check_symbol_exists(accept4 sys/socket.h FOLLY_HAVE_ACCEPT4)
check_symbol_exists(getrandom sys/random.h FOLLY_HAVE_GETRANDOM)
check_symbol_exists(preadv sys/uio.h FOLLY_HAVE_PREADV)
check_symbol_exists(pwritev sys/uio.h FOLLY_HAVE_PWRITEV)
check_symbol_exists(clock_gettime time.h FOLLY_HAVE_CLOCK_GETTIME)
check_symbol_exists(pipe2 unistd.h FOLLY_HAVE_PIPE2)
check_function_exists(malloc_usable_size FOLLY_HAVE_MALLOC_USABLE_SIZE)
set(CMAKE_REQUIRED_FLAGS "${FOLLY_ORIGINAL_CMAKE_REQUIRED_FLAGS}")
check_cxx_source_compiles("
#pragma GCC diagnostic error \"-Wattributes\"
extern \"C\" void (*test_ifunc(void))() { return 0; }
void func() __attribute__((ifunc(\"test_ifunc\")));
int main() { return 0; }"
FOLLY_HAVE_IFUNC
)
check_cxx_source_runs("
int main(int, char**) {
char buf[64] = {0};
unsigned long *ptr = (unsigned long *)(buf + 1);
*ptr = 0xdeadbeef;
return (*ptr & 0xff) == 0xef ? 0 : 1;
}"
FOLLY_HAVE_UNALIGNED_ACCESS
)
check_cxx_source_compiles("
int main(int argc, char** argv) {
unsigned size = argc;
char data[size];
return 0;
}"
FOLLY_HAVE_VLA
)
check_cxx_source_runs("
extern \"C\" int folly_example_undefined_weak_symbol() __attribute__((weak));
int main(int argc, char** argv) {
auto f = folly_example_undefined_weak_symbol; // null pointer
return f ? f() : 0; // must compile, link, and run with null pointer
}"
FOLLY_HAVE_WEAK_SYMBOLS
)
check_cxx_source_runs("
#include
int main() {
void *h = dlopen(\"linux-vdso.so.1\", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (h == nullptr) {
return -1;
}
dlclose(h);
return 0;
}"
FOLLY_HAVE_LINUX_VDSO
)
check_cxx_source_runs("
#include
#include
int main(int argc, char** argv) {
return wcstol(L\"01\", nullptr, 10) == 1 ? 0 : 1;
}"
FOLLY_HAVE_WCHAR_SUPPORT
)
check_cxx_source_compiles("
#include
int main(int argc, char** argv) {
__gnu_cxx::sfmt19937 rng;
return 0;
}"
FOLLY_HAVE_EXTRANDOM_SFMT19937
)
check_cxx_source_runs("
#include
#include
int call_vsnprintf(const char* fmt, ...) {
char buf[256];
va_list ap;
va_start(ap, fmt);
int result = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
return result;
}
int main(int argc, char** argv) {
return call_vsnprintf(\"%\", 1) < 0 ? 0 : 1;
}"
HAVE_VSNPRINTF_ERRORS
)
================================================
FILE: CMake/FollyFunctions.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
function(auto_sources RETURN_VALUE PATTERN SOURCE_SUBDIRS)
if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
SET(PATH ".")
if (${ARGC} EQUAL 4)
list(GET ARGV 3 PATH)
endif ()
endif()
if ("${SOURCE_SUBDIRS}" STREQUAL "RECURSE")
unset(${RETURN_VALUE})
file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
file(GLOB subdirs RELATIVE ${PATH} ${PATH}/*)
foreach(DIR ${subdirs})
if (IS_DIRECTORY ${PATH}/${DIR})
if (NOT "${DIR}" STREQUAL "CMakeFiles")
file(GLOB_RECURSE SUBDIR_FILES "${PATH}/${DIR}/${PATTERN}")
list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
endif()
endif()
endforeach()
else()
file(GLOB ${RETURN_VALUE} "${PATTERN}")
foreach (PATH ${SOURCE_SUBDIRS})
file(GLOB SUBDIR_FILES "${PATH}/${PATTERN}")
list(APPEND ${RETURN_VALUE} ${SUBDIR_FILES})
endforeach()
endif ()
set(${RETURN_VALUE} ${${RETURN_VALUE}} PARENT_SCOPE)
endfunction(auto_sources)
# Remove all files matching a set of patterns, and,
# optionally, not matching a second set of patterns,
# from a set of lists.
#
# Example:
# This will remove all files in the CPP_SOURCES list
# matching "/test/" or "Test.cpp$", but not matching
# "BobTest.cpp$".
# REMOVE_MATCHES_FROM_LISTS(CPP_SOURCES MATCHES "/test/" "Test.cpp$" IGNORE_MATCHES "BobTest.cpp$")
#
# Parameters:
#
# [...]:
# The names of the lists to remove matches from.
#
# [MATCHES ...]:
# The matches to remove from the lists.
#
# [IGNORE_MATCHES ...]:
# The matches not to remove, even if they match
# the main set of matches to remove.
function(REMOVE_MATCHES_FROM_LISTS)
set(LISTS_TO_SEARCH)
set(MATCHES_TO_REMOVE)
set(MATCHES_TO_IGNORE)
set(argumentState 0)
foreach (arg ${ARGN})
if ("x${arg}" STREQUAL "xMATCHES")
set(argumentState 1)
elseif ("x${arg}" STREQUAL "xIGNORE_MATCHES")
set(argumentState 2)
elseif (argumentState EQUAL 0)
list(APPEND LISTS_TO_SEARCH ${arg})
elseif (argumentState EQUAL 1)
list(APPEND MATCHES_TO_REMOVE ${arg})
elseif (argumentState EQUAL 2)
list(APPEND MATCHES_TO_IGNORE ${arg})
else()
message(FATAL_ERROR "Unknown argument state!")
endif()
endforeach()
foreach (theList ${LISTS_TO_SEARCH})
foreach (entry ${${theList}})
foreach (match ${MATCHES_TO_REMOVE})
if (${entry} MATCHES ${match})
set(SHOULD_IGNORE OFF)
foreach (ign ${MATCHES_TO_IGNORE})
if (${entry} MATCHES ${ign})
set(SHOULD_IGNORE ON)
break()
endif()
endforeach()
if (NOT SHOULD_IGNORE)
list(REMOVE_ITEM ${theList} ${entry})
endif()
endif()
endforeach()
endforeach()
set(${theList} ${${theList}} PARENT_SCOPE)
endforeach()
endfunction()
# Automatically create source_group directives for the sources passed in.
function(auto_source_group rootName rootDir)
file(TO_CMAKE_PATH "${rootDir}" rootDir)
string(LENGTH "${rootDir}" rootDirLength)
set(sourceGroups)
foreach (fil ${ARGN})
file(TO_CMAKE_PATH "${fil}" filePath)
string(FIND "${filePath}" "/" rIdx REVERSE)
if (rIdx EQUAL -1)
message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")
endif()
string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)
string(LENGTH "${filePath}" filePathLength)
string(FIND "${filePath}" "${rootDir}" rIdx)
if (rIdx EQUAL 0)
math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")
string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)
string(REPLACE "/" "\\" fileGroup "${fileGroup}")
set(fileGroup "\\${rootName}${fileGroup}")
list(FIND sourceGroups "${fileGroup}" rIdx)
if (rIdx EQUAL -1)
list(APPEND sourceGroups "${fileGroup}")
source_group("${fileGroup}" REGULAR_EXPRESSION "${filePath}/[^/.]+.(cpp|h)$")
endif()
endif()
endforeach()
endfunction()
# CMake is a pain and doesn't have an easy way to install only the files
# we actually included in our build :(
function(auto_install_files rootName rootDir)
file(TO_CMAKE_PATH "${rootDir}" rootDir)
string(LENGTH "${rootDir}" rootDirLength)
set(sourceGroups)
foreach (fil ${ARGN})
file(TO_CMAKE_PATH "${fil}" filePath)
string(FIND "${filePath}" "/" rIdx REVERSE)
if (rIdx EQUAL -1)
message(FATAL_ERROR "Unable to locate the final forward slash in '${filePath}'!")
endif()
string(SUBSTRING "${filePath}" 0 ${rIdx} filePath)
string(LENGTH "${filePath}" filePathLength)
string(FIND "${filePath}" "${rootDir}" rIdx)
if (rIdx EQUAL 0)
math(EXPR filePathLength "${filePathLength} - ${rootDirLength}")
string(SUBSTRING "${filePath}" ${rootDirLength} ${filePathLength} fileGroup)
install(FILES ${fil}
DESTINATION ${INCLUDE_INSTALL_DIR}/${rootName}${fileGroup})
endif()
endforeach()
endfunction()
function(folly_define_tests)
set(directory_count 0)
set(test_count 0)
set(currentArg 0)
while (currentArg LESS ${ARGC})
if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
math(EXPR currentArg "${currentArg} + 1")
if (NOT currentArg LESS ${ARGC})
message(FATAL_ERROR "Expected base directory!")
endif()
set(cur_dir ${directory_count})
math(EXPR directory_count "${directory_count} + 1")
set(directory_${cur_dir}_name "${ARGV${currentArg}}")
# We need a single list of sources to get source_group to work nicely.
set(directory_${cur_dir}_source_list)
math(EXPR currentArg "${currentArg} + 1")
while (currentArg LESS ${ARGC})
if ("x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
break()
elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST" OR
"x${ARGV${currentArg}}" STREQUAL "xBENCHMARK")
set(cur_test ${test_count})
math(EXPR test_count "${test_count} + 1")
set(test_${cur_test}_is_benchmark $)
math(EXPR currentArg "${currentArg} + 1")
if (NOT currentArg LESS ${ARGC})
message(FATAL_ERROR "Expected test name!")
endif()
set(test_${cur_test}_name "${ARGV${currentArg}}")
math(EXPR currentArg "${currentArg} + 1")
set(test_${cur_test}_directory ${cur_dir})
set(test_${cur_test}_content_dir)
set(test_${cur_test}_headers)
set(test_${cur_test}_sources)
set(test_${cur_test}_tag)
set(argumentState 0)
while (currentArg LESS ${ARGC})
if ("x${ARGV${currentArg}}" STREQUAL "xHEADERS")
set(argumentState 1)
elseif ("x${ARGV${currentArg}}" STREQUAL "xSOURCES")
set(argumentState 2)
elseif ("x${ARGV${currentArg}}" STREQUAL "xCONTENT_DIR")
math(EXPR currentArg "${currentArg} + 1")
if (NOT currentArg LESS ${ARGC})
message(FATAL_ERROR "Expected content directory name!")
endif()
set(test_${cur_test}_content_dir "${ARGV${currentArg}}")
elseif ("x${ARGV${currentArg}}" STREQUAL "xTEST" OR
"x${ARGV${currentArg}}" STREQUAL "xBENCHMARK" OR
"x${ARGV${currentArg}}" STREQUAL "xDIRECTORY")
break()
elseif (argumentState EQUAL 0)
if ("x${ARGV${currentArg}}" STREQUAL "xBROKEN")
list(APPEND test_${cur_test}_tag "BROKEN")
elseif ("x${ARGV${currentArg}}" STREQUAL "xHANGING")
list(APPEND test_${cur_test}_tag "HANGING")
elseif ("x${ARGV${currentArg}}" STREQUAL "xSLOW")
list(APPEND test_${cur_test}_tag "SLOW")
elseif ("x${ARGV${currentArg}}" STREQUAL "xWINDOWS_DISABLED")
list(APPEND test_${cur_test}_tag "WINDOWS_DISABLED")
elseif ("x${ARGV${currentArg}}" STREQUAL "xAPPLE_DISABLED")
list(APPEND test_${cur_test}_tag "APPLE_DISABLED")
else()
message(FATAL_ERROR "Unknown test tag '${ARGV${currentArg}}'!")
endif()
elseif (argumentState EQUAL 1)
list(APPEND test_${cur_test}_headers
"${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"
)
elseif (argumentState EQUAL 2)
list(APPEND test_${cur_test}_sources
"${FOLLY_DIR}/${directory_${cur_dir}_name}${ARGV${currentArg}}"
)
else()
message(FATAL_ERROR "Unknown argument state!")
endif()
math(EXPR currentArg "${currentArg} + 1")
endwhile()
list(APPEND directory_${cur_dir}_source_list
${test_${cur_test}_sources} ${test_${cur_test}_headers})
else()
message(FATAL_ERROR "Unknown argument inside directory '${ARGV${currentArg}}'!")
endif()
endwhile()
else()
message(FATAL_ERROR "Unknown argument '${ARGV${currentArg}}'!")
endif()
endwhile()
set(cur_dir 0)
while (cur_dir LESS directory_count)
source_group("" FILES ${directory_${cur_dir}_source_list})
math(EXPR cur_dir "${cur_dir} + 1")
endwhile()
set(cur_test 0)
while (cur_test LESS test_count)
set(cur_test_name ${test_${cur_test}_name})
set(cur_dir_name ${directory_${test_${cur_test}_directory}_name})
if ("BROKEN" IN_LIST test_${cur_test}_tag AND NOT BUILD_BROKEN_TESTS)
message("Skipping broken test ${cur_dir_name}${cur_test_name}, enable with BUILD_BROKEN_TESTS")
elseif ("SLOW" IN_LIST test_${cur_test}_tag AND NOT BUILD_SLOW_TESTS)
message("Skipping slow test ${cur_dir_name}${cur_test_name}, enable with BUILD_SLOW_TESTS")
elseif ("HANGING" IN_LIST test_${cur_test}_tag AND NOT BUILD_HANGING_TESTS)
message("Skipping hanging test ${cur_dir_name}${cur_test_name}, enable with BUILD_HANGING_TESTS")
elseif ("WINDOWS_DISABLED" IN_LIST test_${cur_test}_tag AND WIN32 AND NOT BUILD_WINDOWS_DISABLED)
message("Skipping windows disabled test ${cur_dir_name}${cur_test_name}, enable with BUILD_WINDOWS_DISABLED")
elseif ("APPLE_DISABLED" IN_LIST test_${cur_test}_tag AND APPLE AND NOT BUILD_APPLE_DISABLED)
message("Skipping apple disabled test ${cur_dir_name}${cur_test_name}, enable with BUILD_APPLE_DISABLED")
elseif (${test_${cur_test}_is_benchmark} AND NOT BUILD_BENCHMARKS)
message("Skipping benchmark ${cur_dir_name}${cur_test_name}, enable with BUILD_BENCHMARKS")
else()
add_executable(${cur_test_name}
${test_${cur_test}_headers}
${test_${cur_test}_sources}
)
if (NOT ${test_${cur_test}_is_benchmark})
if (HAVE_CMAKE_GTEST)
# If we have CMake's built-in gtest support use it to add each test
# function as a separate test.
gtest_add_tests(TARGET ${cur_test_name}
WORKING_DIRECTORY "${TOP_DIR}"
TEST_PREFIX "${cur_test_name}."
TEST_LIST test_cases)
set_tests_properties(${test_cases} PROPERTIES TIMEOUT 120)
else()
# Otherwise add each test executable as a single test.
add_test(
NAME ${cur_test_name}
COMMAND ${cur_test_name}
WORKING_DIRECTORY "${TOP_DIR}"
)
set_tests_properties(${cur_test_name} PROPERTIES TIMEOUT 120)
endif()
endif()
if (NOT "x${test_${cur_test}_content_dir}" STREQUAL "x")
# Copy the content directory to the output directory tree so that
# tests can be run easily from Visual Studio without having to change
# the working directory for each test individually.
file(
COPY "${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/folly/${cur_dir_name}${test_${cur_test}_content_dir}"
)
add_custom_command(TARGET ${cur_test_name} POST_BUILD COMMAND
${CMAKE_COMMAND} ARGS -E copy_directory
"${FOLLY_DIR}/${cur_dir_name}${test_${cur_test}_content_dir}"
"$/folly/${cur_dir_name}${test_${cur_test}_content_dir}"
COMMENT "Copying test content for ${cur_test_name}" VERBATIM
)
endif()
# Strip the tailing test directory name for the folder name.
string(REPLACE "test/" "" test_dir_name "${cur_dir_name}")
set_property(TARGET ${cur_test_name} PROPERTY FOLDER "Tests/${test_dir_name}")
target_link_libraries(${cur_test_name} PRIVATE folly_test_support)
apply_folly_compile_options_to_target(${cur_test_name})
endif()
math(EXPR cur_test "${cur_test} + 1")
endwhile()
endfunction()
# Initialize global property to track all granular component targets
define_property(GLOBAL PROPERTY FOLLY_COMPONENT_TARGETS
BRIEF_DOCS "List of all folly component OBJECT targets"
FULL_DOCS "Used to aggregate all component targets into the monolithic folly library"
)
set_property(GLOBAL PROPERTY FOLLY_COMPONENT_TARGETS "")
# Track deferred dependencies to be linked after all targets are created
# Each entry is: "target|visibility|dep1,dep2,dep3"
define_property(GLOBAL PROPERTY FOLLY_DEFERRED_DEPS
BRIEF_DOCS "List of deferred dependency specifications"
FULL_DOCS "Format: target|PUBLIC|PRIVATE|dep1,dep2,..."
)
set_property(GLOBAL PROPERTY FOLLY_DEFERRED_DEPS "")
# Track INTERFACE targets that need to link to monolithic folly (for shared builds)
define_property(GLOBAL PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS
BRIEF_DOCS "List of granular INTERFACE targets for shared builds"
FULL_DOCS "These targets will be linked to the monolithic folly library"
)
set_property(GLOBAL PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS "")
# Helper to add a folly library target
# Creates:
# 1. OBJECT library (${name}_obj) - for composition into monolithic target
# 2. STATIC library (${name}) - individual .a file for granular linking
# Usage:
# folly_add_library(
# NAME iobuf # Target suffix (full name: folly_io_iobuf)
# TARGET_NAME follybenchmark # Optional: override computed target name
# SRCS Cursor.cpp IOBuf.cpp # Source files (optional if using AUTO_SOURCES)
# HEADERS Cursor.h IOBuf.h # Header files
# AUTO_SOURCES # Use auto_sources() instead of explicit SRCS
# DEPS folly_lang_bits # Private dependencies (internal folly targets)
# EXPORTED_DEPS folly_range # Public dependencies (propagated to users)
# EXTERNAL_DEPS ${LIBSODIUM_LIBRARIES} # External library dependencies
# EXTERNAL_INCLUDE_DIRS ${LIBSODIUM_INCLUDE_DIRS} # External include directories
# COMPILE_OPTIONS -mpclmul # Optional compile options for source files
# EXCLUDE_FROM_MONOLITH # Don't include in monolithic folly library
# )
function(folly_add_library)
cmake_parse_arguments(
FOLLY_LIB
"AUTO_SOURCES;EXCLUDE_FROM_MONOLITH" # Options
"NAME;TARGET_NAME" # Single-value args
"SRCS;HEADERS;DEPS;EXPORTED_DEPS;EXTERNAL_DEPS;EXTERNAL_INCLUDE_DIRS;COMPILE_OPTIONS" # Multi-value args
${ARGN}
)
# Use explicit TARGET_NAME if provided, otherwise compute from directory
if(FOLLY_LIB_TARGET_NAME)
set(_target_name "${FOLLY_LIB_TARGET_NAME}")
else()
# Compute target name from current directory relative to FOLLY_DIR
# e.g., folly/io → folly_io, folly/io/async → folly_io_async
file(RELATIVE_PATH _rel_path "${FOLLY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
if(_rel_path STREQUAL "")
set(_target_name "folly_${FOLLY_LIB_NAME}")
else()
string(REPLACE "/" "_" _prefix "${_rel_path}")
set(_target_name "folly_${_prefix}_${FOLLY_LIB_NAME}")
endif()
endif()
# Handle source collection
if(FOLLY_LIB_AUTO_SOURCES)
auto_sources(_srcs "*.cpp" "" "${CMAKE_CURRENT_SOURCE_DIR}")
REMOVE_MATCHES_FROM_LISTS(_srcs MATCHES "test/" "Test.cpp$" "Benchmark.cpp$")
else()
set(_srcs ${FOLLY_LIB_SRCS})
endif()
# Skip if no sources (header-only library)
list(LENGTH _srcs _src_count)
if(_src_count EQUAL 0)
# Header-only: create INTERFACE library
add_library(${_target_name} INTERFACE)
target_include_directories(${_target_name}
INTERFACE
$
$
$
)
target_link_libraries(${_target_name}
INTERFACE folly_deps ${FOLLY_LIB_EXPORTED_DEPS} ${FOLLY_LIB_EXTERNAL_DEPS}
)
# Track external deps for monolithic library (header-only deps are used transitively)
if(FOLLY_LIB_EXTERNAL_DEPS)
set_property(GLOBAL APPEND PROPERTY FOLLY_MONOLITHIC_EXTERNAL_DEPS ${FOLLY_LIB_EXTERNAL_DEPS})
endif()
install(
TARGETS ${_target_name}
EXPORT folly
)
add_library(Folly::${_target_name} ALIAS ${_target_name})
return()
endif()
# 1. Create OBJECT library (for composition into monolithic target)
set(_obj_target "${_target_name}_obj")
add_library(${_obj_target} OBJECT ${_srcs} ${FOLLY_LIB_HEADERS})
set_property(TARGET ${_obj_target} PROPERTY VERSION ${PACKAGE_VERSION})
if(BUILD_SHARED_LIBS)
set_property(TARGET ${_obj_target} PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
target_include_directories(${_obj_target}
PUBLIC
$
$
$
)
apply_folly_compile_options_to_target(${_obj_target})
# Apply optional compile options
if(FOLLY_LIB_COMPILE_OPTIONS)
target_compile_options(${_obj_target} PRIVATE ${FOLLY_LIB_COMPILE_OPTIONS})
endif()
# Link dependencies on OBJECT library
# External deps via folly_deps are always available
# Internal folly deps are deferred until all targets exist
target_link_libraries(${_obj_target}
PUBLIC folly_deps
)
# Link external dependencies (e.g., libsodium, openssl) directly
if(FOLLY_LIB_EXTERNAL_DEPS)
target_link_libraries(${_obj_target}
PUBLIC ${FOLLY_LIB_EXTERNAL_DEPS}
)
endif()
# Add external include directories (e.g., libsodium include dirs)
if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)
target_include_directories(${_obj_target}
PUBLIC ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}
)
endif()
# Defer internal folly dependencies until all targets are created
# (Only for static builds - for shared builds, OBJECT targets are bundled into monolithic folly)
if(NOT BUILD_SHARED_LIBS)
if(FOLLY_LIB_EXPORTED_DEPS)
# Join deps with comma, store as "target|PUBLIC|dep1,dep2,..."
list(JOIN FOLLY_LIB_EXPORTED_DEPS "," _deps_str)
set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS
"${_obj_target}|PUBLIC|${_deps_str}"
)
endif()
if(FOLLY_LIB_DEPS)
list(JOIN FOLLY_LIB_DEPS "," _deps_str)
set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS
"${_obj_target}|PRIVATE|${_deps_str}"
)
endif()
endif()
# Track OBJECT target for monolithic aggregation (unless excluded)
if(NOT FOLLY_LIB_EXCLUDE_FROM_MONOLITH)
set_property(GLOBAL APPEND PROPERTY FOLLY_COMPONENT_TARGETS ${_obj_target})
# Track external deps for the monolithic library
if(FOLLY_LIB_EXTERNAL_DEPS)
set_property(GLOBAL APPEND PROPERTY FOLLY_MONOLITHIC_EXTERNAL_DEPS ${FOLLY_LIB_EXTERNAL_DEPS})
endif()
endif()
# 2. Create the granular library target
if(BUILD_SHARED_LIBS AND NOT FOLLY_LIB_EXCLUDE_FROM_MONOLITH)
# For shared builds: create INTERFACE library that will link to monolithic folly
# This avoids duplicating symbols between granular and monolithic libraries
add_library(${_target_name} INTERFACE)
target_include_directories(${_target_name}
INTERFACE
$
$
$
)
# Add external include directories for INTERFACE target
if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)
target_include_directories(${_target_name}
INTERFACE ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}
)
endif()
# Track this target to link to folly after monolithic library is created
set_property(GLOBAL APPEND PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS ${_target_name})
# Install the INTERFACE library
install(
TARGETS ${_target_name}
EXPORT folly
)
elseif(BUILD_SHARED_LIBS AND FOLLY_LIB_EXCLUDE_FROM_MONOLITH)
# For excluded targets in shared builds: create SHARED library with actual code
# These are NOT in the monolithic folly, so they need their own implementation
add_library(${_target_name} SHARED $)
set_property(TARGET ${_target_name} PROPERTY VERSION ${PACKAGE_VERSION})
target_include_directories(${_target_name}
PUBLIC
$
$
$
)
# Link to folly_deps (external dependencies)
target_link_libraries(${_target_name}
PUBLIC folly_deps
)
# Link external dependencies
if(FOLLY_LIB_EXTERNAL_DEPS)
target_link_libraries(${_target_name}
PUBLIC ${FOLLY_LIB_EXTERNAL_DEPS}
)
endif()
# Add external include directories
if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)
target_include_directories(${_target_name}
PUBLIC ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}
)
endif()
# Defer linking to folly (created later by folly_create_monolithic_library)
# Also defer internal folly dependencies
set(_all_deps "folly")
if(FOLLY_LIB_EXPORTED_DEPS)
list(APPEND _all_deps ${FOLLY_LIB_EXPORTED_DEPS})
endif()
list(JOIN _all_deps "," _deps_str)
set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS
"${_target_name}|PUBLIC|${_deps_str}"
)
if(FOLLY_LIB_DEPS)
list(JOIN FOLLY_LIB_DEPS "," _deps_str)
set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS
"${_target_name}|PRIVATE|${_deps_str}"
)
endif()
# Install the SHARED library
install(
TARGETS ${_target_name}
EXPORT folly
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
)
else()
# For static builds: create STATIC library (individual .a file for granular linking)
add_library(${_target_name} STATIC $)
set_property(TARGET ${_target_name} PROPERTY VERSION ${PACKAGE_VERSION})
target_include_directories(${_target_name}
PUBLIC
$
$
$
)
# Link external dependencies on STATIC library
# Internal folly deps are deferred (see above)
target_link_libraries(${_target_name}
PUBLIC folly_deps
)
# Link external dependencies (e.g., libsodium, openssl) directly
if(FOLLY_LIB_EXTERNAL_DEPS)
target_link_libraries(${_target_name}
PUBLIC ${FOLLY_LIB_EXTERNAL_DEPS}
)
endif()
# Add external include directories for STATIC target
if(FOLLY_LIB_EXTERNAL_INCLUDE_DIRS)
target_include_directories(${_target_name}
PUBLIC ${FOLLY_LIB_EXTERNAL_INCLUDE_DIRS}
)
endif()
# Defer internal folly dependencies for STATIC library too
if(FOLLY_LIB_EXPORTED_DEPS)
list(JOIN FOLLY_LIB_EXPORTED_DEPS "," _deps_str)
set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS
"${_target_name}|PUBLIC|${_deps_str}"
)
endif()
if(FOLLY_LIB_DEPS)
list(JOIN FOLLY_LIB_DEPS "," _deps_str)
set_property(GLOBAL APPEND PROPERTY FOLLY_DEFERRED_DEPS
"${_target_name}|PRIVATE|${_deps_str}"
)
endif()
# Install the STATIC library
install(
TARGETS ${_target_name}
EXPORT folly
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
)
endif()
# Create alias for the library
add_library(Folly::${_target_name} ALIAS ${_target_name})
endfunction()
# Resolve all deferred dependencies after all targets have been created
# Call this after all add_subdirectory() calls
function(folly_resolve_deferred_dependencies)
# Allow linking targets defined in other directories
cmake_policy(SET CMP0079 NEW)
get_property(_deferred_deps GLOBAL PROPERTY FOLLY_DEFERRED_DEPS)
foreach(_spec IN LISTS _deferred_deps)
# Parse the spec: "target|visibility|dep1,dep2,..."
string(REPLACE "|" ";" _parts "${_spec}")
list(LENGTH _parts _len)
if(_len LESS 3)
continue()
endif()
list(GET _parts 0 _target)
list(GET _parts 1 _visibility)
list(GET _parts 2 _deps_str)
# Split deps by comma
string(REPLACE "," ";" _deps "${_deps_str}")
# Filter to only existing targets (skip deps that weren't generated)
set(_valid_deps "")
foreach(_dep IN LISTS _deps)
if(TARGET ${_dep})
list(APPEND _valid_deps ${_dep})
endif()
endforeach()
if(_valid_deps)
target_link_libraries(${_target} ${_visibility} ${_valid_deps})
endif()
endforeach()
endfunction()
# Create the monolithic folly library from all component OBJECT libraries
# Call this after all add_subdirectory() calls and folly_resolve_deferred_dependencies()
function(folly_create_monolithic_library)
get_property(_component_targets GLOBAL PROPERTY FOLLY_COMPONENT_TARGETS)
# Collect all object files from component targets
set(_all_objects)
foreach(_target IN LISTS _component_targets)
list(APPEND _all_objects $)
endforeach()
# Create the monolithic library
add_library(folly ${_all_objects})
if(BUILD_SHARED_LIBS)
set_property(TARGET folly PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()
set_property(TARGET folly PROPERTY VERSION ${PACKAGE_VERSION})
apply_folly_compile_options_to_target(folly)
target_compile_features(folly INTERFACE cxx_generic_lambdas)
target_include_directories(folly
PUBLIC
$
$
$
)
target_link_libraries(folly PUBLIC folly_deps)
# Link all external dependencies that were tracked from component targets
get_property(_external_deps GLOBAL PROPERTY FOLLY_MONOLITHIC_EXTERNAL_DEPS)
if(_external_deps)
list(REMOVE_DUPLICATES _external_deps)
target_link_libraries(folly PUBLIC ${_external_deps})
endif()
# Create alias for consistency
add_library(Folly::folly ALIAS folly)
# For shared builds: link all granular INTERFACE targets to the monolithic library
if(BUILD_SHARED_LIBS)
# CMP0079: target_link_libraries allows use with targets in other directories
cmake_policy(SET CMP0079 NEW)
get_property(_interface_targets GLOBAL PROPERTY FOLLY_GRANULAR_INTERFACE_TARGETS)
foreach(_target IN LISTS _interface_targets)
target_link_libraries(${_target} INTERFACE folly)
endforeach()
endif()
endfunction()
================================================
FILE: CMake/GenPkgConfig.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Generate variables that can be used to help emit a pkg-config file
# using configure_file().
#
# Usage: gen_pkgconfig_vars(VAR_PREFIX target)
#
# This will set two variables in the caller scope:
# ${VAR_PREFIX}_CFLAGS: set to the compile flags computed from the specified
# target
# ${VAR_PREFIX}_PRIVATE_LIBS: set to the linker flags needed for static
# linking computed from the specified target
function(gen_pkgconfig_vars)
if (NOT ${ARGC} EQUAL 2)
message(FATAL_ERROR "gen_pkgconfig_vars() requires exactly 2 arguments")
endif()
set(var_prefix "${ARGV0}")
set(target "${ARGV1}")
get_target_property(target_cflags "${target}" INTERFACE_COMPILE_OPTIONS)
if(target_cflags)
list(APPEND cflags "${target_cflags}")
endif()
get_target_property(
target_inc_dirs "${target}" INTERFACE_INCLUDE_DIRECTORIES)
if(target_inc_dirs)
list(APPEND include_dirs "${target_inc_dirs}")
endif()
get_target_property(target_defns "${target}" INTERFACE_COMPILE_DEFINITIONS)
if(target_defns)
list(APPEND definitions "${target_defns}")
endif()
# The INTERFACE_LINK_LIBRARIES list is unfortunately somewhat awkward to
# process. Entries in this list may be any of
# - target names
# - absolute paths to a library file
# - plain library names that need "-l" prepended
# - other linker flags starting with "-"
#
# Walk through each entry and transform it into the desired arguments
get_target_property(link_libs "${target}" INTERFACE_LINK_LIBRARIES)
if(link_libs)
foreach(lib_arg IN LISTS link_libs)
if(TARGET "${lib_arg}")
# Add any compile options specified in the targets
# INTERFACE_COMPILE_OPTIONS. We don't need to process its
# INTERFACE_LINK_LIBRARIES property, since our INTERFACE_LINK_LIBRARIES
# will already include its entries transitively.
get_target_property(lib_cflags "${lib_arg}" INTERFACE_COMPILE_OPTIONS)
if(lib_cflags)
list(APPEND cflags "${lib_cflags}")
endif()
get_target_property(lib_defs "${lib_arg}"
INTERFACE_COMPILE_DEFINITIONS)
if(lib_defs)
list(APPEND definitions "${lib_defs}")
endif()
elseif(lib_arg MATCHES "^[-/]")
list(APPEND private_libs "${lib_arg}")
else()
list(APPEND private_libs "-l${lib_arg}")
endif()
endforeach()
endif()
list(APPEND cflags "${CMAKE_REQUIRED_FLAGS}")
if(definitions)
list(REMOVE_DUPLICATES definitions)
foreach(def_arg IN LISTS definitions)
list(APPEND cflags "-D${def_arg}")
endforeach()
endif()
if(include_dirs)
list(REMOVE_DUPLICATES include_dirs)
foreach(inc_dir IN LISTS include_dirs)
list(APPEND cflags "-I${inc_dir}")
endforeach()
endif()
# Set the output variables
string(REPLACE ";" " " cflags "${cflags}")
string(REPLACE ";" " " private_libs "${private_libs}")
# Since CMake 3.18 FindThreads may include a generator expression requiring
# a target, which gets propagated to us through INTERFACE_COMPILE_OPTIONS.
# Before CMake 3.19 there's no way to solve this in a general way, so we
# work around the specific case. See #1414 and CMake bug #21074.
if(CMAKE_VERSION VERSION_LESS 3.19)
string(REPLACE
"" ""
cflags "${cflags}"
)
endif()
# patch for fmt's generator expression
if (MSVC)
# fmt 11.0.3 and above
string(REPLACE "$<$,$>:/utf-8>" "/utf-8" cflags "${cflags}")
# fmt 11.0.2 and below
string(REPLACE "$<$:/utf-8>" "/utf-8" cflags "${cflags}")
endif()
set("${var_prefix}_CFLAGS" "${cflags}" PARENT_SCOPE)
set("${var_prefix}_PRIVATE_LIBS" "${private_libs}" PARENT_SCOPE)
endfunction()
================================================
FILE: CMake/folly-config.cmake.in
================================================
# CMake configuration file for folly
#
# This provides the Folly::folly target, which you can depend on by adding it
# to your target_link_libraries().
#
# It also defines the following variables, although using these directly is not
# necessary if you use the Folly::folly target instead.
# FOLLY_INCLUDE_DIR
# FOLLY_LIBRARIES
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
set_and_check(FOLLY_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(FOLLY_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@")
set_and_check(FOLLY_CMAKE_DIR "@PACKAGE_CMAKE_INSTALL_DIR@")
# find_dependency() ends up changing PACKAGE_PREFIX_DIR, so save
# folly's prefix directory in the FOLLY_PREFIX_DIR variable
set(FOLLY_PREFIX_DIR "${PACKAGE_PREFIX_DIR}")
# Find glog before loading targets, since targets reference the glog::glog target
if(NOT TARGET glog::glog)
find_package(Glog QUIET)
endif()
# Include the folly-targets.cmake file, which is generated from our CMake rules
if (NOT TARGET Folly::folly)
include("${FOLLY_CMAKE_DIR}/folly-targets.cmake")
endif()
# Set FOLLY_LIBRARIES from our Folly::folly target
set(FOLLY_LIBRARIES Folly::folly)
# Find folly's dependencies
find_dependency(fmt)
set(Boost_USE_STATIC_LIBS "@FOLLY_BOOST_LINK_STATIC@")
find_package(Boost 1.69.0 REQUIRED
COMPONENTS
context
filesystem
program_options
regex
thread
)
if (NOT folly_FIND_QUIETLY)
message(STATUS "Found folly: ${FOLLY_PREFIX_DIR}")
endif()
================================================
FILE: CMake/folly-config.h.cmake
================================================
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#ifdef __APPLE__
#include // @manual
#endif
#if !defined(FOLLY_MOBILE)
#if defined(__ANDROID__) || \
(defined(__APPLE__) && \
(TARGET_IPHONE_SIMULATOR || TARGET_OS_SIMULATOR || TARGET_OS_IPHONE))
#define FOLLY_MOBILE 1
#else
#define FOLLY_MOBILE 0
#endif
#endif // FOLLY_MOBILE
#cmakedefine01 FOLLY_HAVE_PTHREAD
#cmakedefine FOLLY_HAVE_PTHREAD_ATFORK 1
#cmakedefine FOLLY_HAVE_LIBGFLAGS 1
#cmakedefine FOLLY_HAVE_LIBGLOG 1
#cmakedefine FOLLY_USE_JEMALLOC 1
#if __has_include()
#include
#endif
#cmakedefine FOLLY_HAVE_ACCEPT4 1
#cmakedefine01 FOLLY_HAVE_GETRANDOM
#cmakedefine FOLLY_HAVE_PREADV 1
#cmakedefine FOLLY_HAVE_PWRITEV 1
#cmakedefine01 FOLLY_HAVE_CLOCK_GETTIME
#cmakedefine FOLLY_HAVE_PIPE2 1
#cmakedefine FOLLY_HAVE_IFUNC 1
#cmakedefine FOLLY_HAVE_UNALIGNED_ACCESS 1
#cmakedefine FOLLY_HAVE_VLA 1
#cmakedefine01 FOLLY_HAVE_WEAK_SYMBOLS
#cmakedefine FOLLY_HAVE_LINUX_VDSO 1
#cmakedefine FOLLY_HAVE_MALLOC_USABLE_SIZE 1
#cmakedefine FOLLY_HAVE_INT128_T 1
#cmakedefine FOLLY_HAVE_WCHAR_SUPPORT 1
#cmakedefine FOLLY_HAVE_EXTRANDOM_SFMT19937 1
#cmakedefine HAVE_VSNPRINTF_ERRORS 1
#cmakedefine FOLLY_HAVE_LIBUNWIND 1
#cmakedefine01 FOLLY_HAVE_DWARF
#cmakedefine01 FOLLY_HAVE_ELF
#cmakedefine FOLLY_HAVE_SWAPCONTEXT 1
#cmakedefine FOLLY_HAVE_BACKTRACE 1
#cmakedefine FOLLY_USE_SYMBOLIZER 1
#define FOLLY_DEMANGLE_MAX_SYMBOL_SIZE 1024
#cmakedefine FOLLY_HAVE_SHADOW_LOCAL_WARNINGS 1
#cmakedefine01 FOLLY_HAVE_LIBLZ4
#cmakedefine01 FOLLY_HAVE_LIBLZMA
#cmakedefine01 FOLLY_HAVE_LIBSNAPPY
#cmakedefine01 FOLLY_HAVE_LIBZ
#cmakedefine01 FOLLY_HAVE_LIBZSTD
#cmakedefine01 FOLLY_HAVE_LIBBZ2
#cmakedefine01 FOLLY_LIBRARY_SANITIZE_ADDRESS
#cmakedefine FOLLY_SUPPORT_SHARED_LIBRARY 1
#cmakedefine01 FOLLY_HAVE_LIBRT
#cmakedefine01 FOLLY_HAVE_VSOCK
================================================
FILE: CMake/folly-deps.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include(CheckCXXSourceCompiles)
include(CheckCXXSymbolExists)
include(CheckIncludeFileCXX)
include(CheckFunctionExists)
include(CMakePushCheckState)
set(
BOOST_LINK_STATIC "auto"
CACHE STRING
"Whether to link against boost statically or dynamically."
)
if("${BOOST_LINK_STATIC}" STREQUAL "auto")
# Default to linking boost statically on Windows with MSVC
if(MSVC)
set(FOLLY_BOOST_LINK_STATIC ON)
else()
set(FOLLY_BOOST_LINK_STATIC OFF)
endif()
else()
set(FOLLY_BOOST_LINK_STATIC "${BOOST_LINK_STATIC}")
endif()
set(Boost_USE_STATIC_LIBS "${FOLLY_BOOST_LINK_STATIC}")
# Note: We find these components so the CMake targets exist, but we don't
# link them globally. Targets that need specific Boost libraries should
# add them to their EXTERNAL_DEPS (e.g., Boost::regex, Boost::context).
# Boost::thread is needed by Windows pthread compatibility layer.
set(FOLLY_BOOST_COMPONENTS
context
filesystem
program_options
regex
)
if(WIN32)
list(APPEND FOLLY_BOOST_COMPONENTS thread)
endif()
find_package(Boost 1.69.0 REQUIRED
COMPONENTS
${FOLLY_BOOST_COMPONENTS}
)
# Only add include directories globally, not libraries
# Per-target Boost dependencies are specified via EXTERNAL_DEPS
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS})
find_package(DoubleConversion MODULE REQUIRED)
list(APPEND FOLLY_LINK_LIBRARIES ${DOUBLE_CONVERSION_LIBRARY})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${DOUBLE_CONVERSION_INCLUDE_DIR})
find_package(FastFloat MODULE REQUIRED)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${FASTFLOAT_INCLUDE_DIR})
find_package(Gflags MODULE)
set(FOLLY_HAVE_LIBGFLAGS ${LIBGFLAGS_FOUND})
if(LIBGFLAGS_FOUND)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBGFLAGS_LIBRARY})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBGFLAGS_INCLUDE_DIR})
set(FOLLY_LIBGFLAGS_LIBRARY ${LIBGFLAGS_LIBRARY})
set(FOLLY_LIBGFLAGS_INCLUDE ${LIBGFLAGS_INCLUDE_DIR})
endif()
find_package(Glog MODULE)
set(FOLLY_HAVE_LIBGLOG ${GLOG_FOUND})
list(APPEND FOLLY_LINK_LIBRARIES glog::glog)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${GLOG_INCLUDE_DIR})
# Glog 0.7+ requires GLOG_USE_GLOG_EXPORT to be defined so that headers
# include glog/export.h which defines GLOG_EXPORT.
if (EXISTS "${GLOG_INCLUDE_DIR}/glog/export.h")
list(APPEND FOLLY_CXX_FLAGS -DGLOG_USE_GLOG_EXPORT)
endif()
find_package(LibEvent MODULE REQUIRED)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBEVENT_LIB})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBEVENT_INCLUDE_DIR})
find_package(ZLIB MODULE)
set(FOLLY_HAVE_LIBZ ${ZLIB_FOUND})
if (ZLIB_FOUND)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${ZLIB_INCLUDE_DIRS})
list(APPEND FOLLY_LINK_LIBRARIES ${ZLIB_LIBRARIES})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${ZLIB_LIBRARIES})
endif()
find_package(OpenSSL 1.1.1 MODULE REQUIRED)
list(APPEND FOLLY_LINK_LIBRARIES ${OPENSSL_LIBRARIES})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${OPENSSL_INCLUDE_DIR})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
list(APPEND CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
if (ZLIB_FOUND)
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${ZLIB_LIBRARIES})
endif()
find_package(BZip2 MODULE)
set(FOLLY_HAVE_LIBBZ2 ${BZIP2_FOUND})
if (BZIP2_FOUND)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${BZIP2_INCLUDE_DIRS})
list(APPEND FOLLY_LINK_LIBRARIES ${BZIP2_LIBRARIES})
endif()
find_package(LibLZMA MODULE)
set(FOLLY_HAVE_LIBLZMA ${LIBLZMA_FOUND})
if (LIBLZMA_FOUND)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBLZMA_INCLUDE_DIRS})
list(APPEND FOLLY_LINK_LIBRARIES ${LIBLZMA_LIBRARIES})
endif()
find_package(LZ4 MODULE)
set(FOLLY_HAVE_LIBLZ4 ${LZ4_FOUND})
if (LZ4_FOUND)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LZ4_INCLUDE_DIR})
list(APPEND FOLLY_LINK_LIBRARIES ${LZ4_LIBRARY})
endif()
find_package(Zstd MODULE)
set(FOLLY_HAVE_LIBZSTD ${ZSTD_FOUND})
if(ZSTD_FOUND)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIR})
list(APPEND FOLLY_LINK_LIBRARIES ${ZSTD_LIBRARY})
endif()
find_package(Snappy MODULE)
set(FOLLY_HAVE_LIBSNAPPY ${SNAPPY_FOUND})
if (SNAPPY_FOUND)
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${SNAPPY_INCLUDE_DIR})
list(APPEND FOLLY_LINK_LIBRARIES ${SNAPPY_LIBRARY})
endif()
find_package(LibDwarf)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBDWARF_LIBRARIES})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBDWARF_INCLUDE_DIRS})
find_package(Libiberty)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBIBERTY_LIBRARIES})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBIBERTY_INCLUDE_DIRS})
find_package(LibAIO)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBAIO_LIBRARIES})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBAIO_INCLUDE_DIRS})
find_package(LibUring)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBURING_LIBRARIES})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBURING_INCLUDE_DIRS})
find_package(Libsodium)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBSODIUM_LIBRARIES})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBSODIUM_INCLUDE_DIRS})
list(APPEND FOLLY_LINK_LIBRARIES ${CMAKE_DL_LIBS})
list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS})
if (PYTHON_EXTENSIONS)
find_package(Python3 COMPONENTS Interpreter Development REQUIRED)
find_package(Cython 0.26 REQUIRED)
endif ()
find_package(LibUnwind)
list(APPEND FOLLY_LINK_LIBRARIES ${LIBUNWIND_LIBRARIES})
list(APPEND FOLLY_INCLUDE_DIRECTORIES ${LIBUNWIND_INCLUDE_DIRS})
if (LIBUNWIND_FOUND)
set(FOLLY_HAVE_LIBUNWIND ON)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
list(APPEND FOLLY_LINK_LIBRARIES "execinfo")
endif ()
cmake_push_check_state()
set(CMAKE_REQUIRED_DEFINITIONS -D_XOPEN_SOURCE)
check_cxx_symbol_exists(swapcontext ucontext.h FOLLY_HAVE_SWAPCONTEXT)
cmake_pop_check_state()
set(FOLLY_USE_SYMBOLIZER OFF)
CHECK_INCLUDE_FILE_CXX(elf.h FOLLY_HAVE_ELF)
find_package(Backtrace)
set(FOLLY_HAVE_BACKTRACE ${Backtrace_FOUND})
set(FOLLY_HAVE_DWARF ${LIBDWARF_FOUND})
if (NOT WIN32 AND NOT APPLE)
set(FOLLY_USE_SYMBOLIZER ON)
endif()
message(STATUS "Setting FOLLY_USE_SYMBOLIZER: ${FOLLY_USE_SYMBOLIZER}")
message(STATUS "Setting FOLLY_HAVE_ELF: ${FOLLY_HAVE_ELF}")
message(STATUS "Setting FOLLY_HAVE_DWARF: ${FOLLY_HAVE_DWARF}")
# Using clang with libstdc++ requires explicitly linking against libatomic
check_cxx_source_compiles("
#include
int main(int argc, char** argv) {
std::atomic a1;
std::atomic a2;
std::atomic a4;
std::atomic a8;
struct Test { bool val; };
std::atomic s;
return a1++ + a2++ + a4++ + a8++ + unsigned(s.is_lock_free());
}"
FOLLY_CPP_ATOMIC_BUILTIN
)
if(NOT FOLLY_CPP_ATOMIC_BUILTIN)
list(APPEND CMAKE_REQUIRED_LIBRARIES atomic)
list(APPEND FOLLY_LINK_LIBRARIES atomic)
set(ATOMIC_LIBRARY "atomic")
check_cxx_source_compiles("
#include
int main(int argc, char** argv) {
std::atomic a1;
std::atomic a2;
std::atomic a4;
std::atomic a8;
struct Test { bool val; };
std::atomic s;
return a1++ + a2++ + a4++ + a8++ + unsigned(s.is_lock_free());
}"
FOLLY_CPP_ATOMIC_WITH_LIBATOMIC
)
if (NOT FOLLY_CPP_ATOMIC_WITH_LIBATOMIC)
message(
FATAL_ERROR "unable to link C++ std::atomic code: you may need \
to install GNU libatomic"
)
endif()
endif()
check_cxx_source_compiles("
#include
#if _GLIBCXX_RELEASE
int main() {}
#endif"
FOLLY_STDLIB_LIBSTDCXX
)
check_cxx_source_compiles("
#include
#if _GLIBCXX_RELEASE >= 9
int main() {}
#endif"
FOLLY_STDLIB_LIBSTDCXX_GE_9
)
check_cxx_source_compiles("
#include
#if _LIBCPP_VERSION
int main() {}
#endif"
FOLLY_STDLIB_LIBCXX
)
check_cxx_source_compiles("
#include
#if _LIBCPP_VERSION >= 9000
int main() {}
#endif"
FOLLY_STDLIB_LIBCXX_GE_9
)
check_cxx_source_compiles("
#include
#if _CPPLIB_VER
int main() {}
#endif"
FOLLY_STDLIB_LIBCPP
)
if (APPLE)
list (APPEND CMAKE_REQUIRED_LIBRARIES c++abi)
list (APPEND FOLLY_LINK_LIBRARIES c++abi)
endif ()
if (FOLLY_STDLIB_LIBSTDCXX AND NOT FOLLY_STDLIB_LIBSTDCXX_GE_9)
list (APPEND CMAKE_REQUIRED_LIBRARIES stdc++fs)
list (APPEND FOLLY_LINK_LIBRARIES stdc++fs)
endif()
if (FOLLY_STDLIB_LIBCXX AND NOT FOLLY_STDLIB_LIBCXX_GE_9)
list (APPEND CMAKE_REQUIRED_LIBRARIES c++fs)
list (APPEND FOLLY_LINK_LIBRARIES c++fs)
endif ()
option(
FOLLY_LIBRARY_SANITIZE_ADDRESS
"Build folly with Address Sanitizer enabled."
OFF
)
if ($ENV{WITH_ASAN})
message(STATUS "ENV WITH_ASAN is set")
set (FOLLY_LIBRARY_SANITIZE_ADDRESS ON)
endif()
if (FOLLY_LIBRARY_SANITIZE_ADDRESS)
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES GNU)
set(FOLLY_LIBRARY_SANITIZE_ADDRESS ON)
set(FOLLY_ASAN_FLAGS -fsanitize=address,undefined)
list(APPEND FOLLY_CXX_FLAGS ${FOLLY_ASAN_FLAGS})
# All of the functions in folly/detail/Sse.cpp are intended to be compiled
# with ASAN disabled. They are marked with attributes to disable the
# sanitizer, but even so, gcc fails to compile them for some reason when
# sanitization is enabled on the compile line.
set_source_files_properties(
"${PROJECT_SOURCE_DIR}/folly/detail/Sse.cpp"
PROPERTIES COMPILE_FLAGS -fno-sanitize=address,undefined
)
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES Clang)
set(FOLLY_LIBRARY_SANITIZE_ADDRESS ON)
set(
FOLLY_ASAN_FLAGS
-fno-common
-fsanitize=address,undefined,integer,nullability
-fno-sanitize=unsigned-integer-overflow
)
list(APPEND FOLLY_CXX_FLAGS ${FOLLY_ASAN_FLAGS})
endif()
endif()
add_library(folly_deps INTERFACE)
find_package(fmt CONFIG)
if (NOT DEFINED fmt_CONFIG)
# Fallback on a normal search on the current system
find_package(Fmt MODULE REQUIRED)
endif()
target_link_libraries(folly_deps INTERFACE fmt::fmt)
list(REMOVE_DUPLICATES FOLLY_INCLUDE_DIRECTORIES)
if(NOT "${CMAKE_SOURCE_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}")
# When consumed via add_subdirectory/FetchContent, wrap each include
# directory in BUILD_INTERFACE so absolute build-tree paths don't leak
# into the parent project's install-time INTERFACE_INCLUDE_DIRECTORIES.
foreach(_dir IN LISTS FOLLY_INCLUDE_DIRECTORIES)
target_include_directories(folly_deps INTERFACE $)
endforeach()
else()
target_include_directories(folly_deps INTERFACE ${FOLLY_INCLUDE_DIRECTORIES})
endif()
target_link_libraries(folly_deps INTERFACE
${FOLLY_LINK_LIBRARIES}
${FOLLY_SHINY_DEPENDENCIES}
${FOLLY_ASAN_FLAGS}
)
================================================
FILE: CMake/libfolly.pc.in
================================================
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@LIB_INSTALL_DIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
Name: libfolly
Description: Facebook (Folly) C++ library
Version: @PACKAGE_VERSION@
Cflags: -I${includedir} @FOLLY_PKGCONFIG_CFLAGS@
Libs: -L${libdir} -lfolly
Libs.private: @FOLLY_PKGCONFIG_PRIVATE_LIBS@
================================================
FILE: CMakeLists.txt
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# We use the GoogleTest module if it is available (only in CMake 3.9+)
# It requires CMP0054 and CMP0057 to be enabled.
if (POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
if (POLICY CMP0057)
cmake_policy(SET CMP0057 NEW)
endif()
# CMP0075 Include file check macros honor CMAKE_REQUIRED_LIBRARIES
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
# CMP0167 The FindBoost module is removed
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
# includes
set(CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/CMake"
# for in-fbsource builds
"${CMAKE_CURRENT_SOURCE_DIR}/../opensource/fbcode_builder/CMake"
# For shipit-transformed builds
"${CMAKE_CURRENT_SOURCE_DIR}/build/fbcode_builder/CMake"
${CMAKE_MODULE_PATH})
# package information
set(PACKAGE_NAME "folly")
if (NOT DEFINED PACKAGE_VERSION)
set(PACKAGE_VERSION "0.58.0-dev")
endif()
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}")
set(PACKAGE_BUGREPORT "https://github.com/facebook/folly/issues")
# 150+ tests in the root folder anyone? No? I didn't think so.
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
project(${PACKAGE_NAME} CXX C ASM)
# Set for FetchContent to skip find_package(folly)
set(folly_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}"
CACHE INTERNAL "folly source directory")
set(INCLUDE_INSTALL_DIR include CACHE STRING
"The subdirectory where header files should be installed")
set(LIB_INSTALL_DIR lib CACHE STRING
"The subdirectory where libraries should be installed")
set(BIN_INSTALL_DIR bin CACHE STRING
"The subdirectory where binaries should be installed")
set(CMAKE_INSTALL_DIR lib/cmake/folly CACHE STRING
"The subdirectory where CMake package config files should be installed")
option(BUILD_SHARED_LIBS
"If enabled, build folly as a shared library. \
This is generally discouraged, since folly does not commit to having \
a stable ABI."
OFF
)
option(PYTHON_EXTENSIONS
"Build Python Bindings for Folly, requires Cython and (BUILD_SHARED_LIBS=ON)"
OFF
)
# Python extensions require shared libraries for proper symbol resolution
if (PYTHON_EXTENSIONS)
set(BUILD_SHARED_LIBS ON CACHE BOOL
"Forced ON because PYTHON_EXTENSIONS requires shared libraries" FORCE)
endif()
set(PYTHON_PACKAGE_INSTALL_DIR "" CACHE STRING
"Installation directory for folly Python packages. \
If empty, CMAKE_INSTALL_PREFIX will be used. \
No effect if PYTHON_EXTENSIONS=OFF."
)
# Mark BUILD_SHARED_LIBS as an "advanced" option, since enabling it
# is generally discouraged.
mark_as_advanced(BUILD_SHARED_LIBS)
set(FOLLY_SUPPORT_SHARED_LIBRARY "${BUILD_SHARED_LIBS}")
include(FBBuildOptions)
fb_activate_static_library_option()
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
message(STATUS "setting C++ standard to C++${CMAKE_CXX_STANDARD}")
endif()
if(NOT DEFINED IS_X86_64_ARCH AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64|AMD64")
set(IS_X86_64_ARCH TRUE)
else()
set(IS_X86_64_ARCH FALSE)
endif()
if(NOT DEFINED IS_AARCH64_ARCH AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64")
set(IS_AARCH64_ARCH TRUE)
else()
set(IS_AARCH64_ARCH FALSE)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# Check target architecture
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "Folly requires a 64bit target architecture.")
endif()
if (MSVC_VERSION LESS 1900)
message(
FATAL_ERROR
"This build script only supports building Folly on 64-bit Windows with "
"at least Visual Studio 2017. "
"MSVC version '${MSVC_VERSION}' is not supported."
)
endif()
endif()
if(NOT DEFINED FOLLY_HAVE_INT128_T)
include(CheckTypeSize)
check_type_size("__int128_t" FOLLY_INT128_T_SIZE)
if(HAVE_FOLLY_INT128_T_SIZE)
set(FOLLY_HAVE_INT128_T TRUE)
endif()
endif()
set(TOP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(FOLLY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/folly")
set(
FOLLY_DIR_PREFIXES
"${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_CURRENT_BINARY_DIR}"
)
# https://gitlab.kitware.com/cmake/cmake/-/issues/18580#note_1405108
string(REGEX REPLACE "(.)" "\\\\\\1" FOLLY_DIR_REGEX_ESCAPED "${FOLLY_DIR}")
include(GNUInstallDirs)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
set(FOLLY_HAVE_PTHREAD "${CMAKE_USE_PTHREADS_INIT}")
list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads)
list(APPEND FOLLY_LINK_LIBRARIES Threads::Threads)
if(MSVC)
include(FollyCompilerMSVC)
else()
include(FollyCompilerUnix)
endif()
include(FollyFunctions)
include(folly-deps) # Find the required packages
include(FollyConfigChecks)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/CMake/folly-config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h
)
# Define FOLLY_XLOG_STRIP_PREFIXES when compiling our sources so that
# folly/logging will automatically choose the correct log category names,
# using only the relative portion of the source file name inside the
# folly repository.
set_property(
DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
APPEND
PROPERTY
COMPILE_DEFINITIONS
"FOLLY_XLOG_STRIP_PREFIXES=\"${CMAKE_SOURCE_DIR}:${CMAKE_BINARY_DIR}\""
)
# Collect headers for installation (still needed for install step)
auto_sources(hfiles "*.h" "RECURSE" "${FOLLY_DIR}")
REMOVE_MATCHES_FROM_LISTS(hfiles
MATCHES
"^${FOLLY_DIR_REGEX_ESCAPED}/build/"
"^${FOLLY_DIR_REGEX_ESCAPED}/docs/examples/"
"^${FOLLY_DIR_REGEX_ESCAPED}/logging/example/"
"^${FOLLY_DIR_REGEX_ESCAPED}/(.*/)?test/"
"^${FOLLY_DIR_REGEX_ESCAPED}/(.*/)?tool/"
"^${FOLLY_DIR_REGEX_ESCAPED}/facebook/"
"^${FOLLY_DIR_REGEX_ESCAPED}/rust/"
"^${FOLLY_DIR_REGEX_ESCAPED}/ext/buck2/"
)
# Add test utility headers that are part of public API
list(APPEND hfiles
${FOLLY_DIR}/container/test/F14TestUtil.h
${FOLLY_DIR}/container/test/TrackingTypes.h
${FOLLY_DIR}/io/async/test/AsyncSSLSocketTest.h
${FOLLY_DIR}/io/async/test/AsyncSocketTest.h
${FOLLY_DIR}/io/async/test/AsyncSocketTest2.h
${FOLLY_DIR}/io/async/test/BlockingSocket.h
${FOLLY_DIR}/io/async/test/CallbackStateEnum.h
${FOLLY_DIR}/io/async/test/ConnCallback.h
${FOLLY_DIR}/io/async/test/MockAsyncSocket.h
${FOLLY_DIR}/io/async/test/MockAsyncServerSocket.h
${FOLLY_DIR}/io/async/test/MockAsyncSSLSocket.h
${FOLLY_DIR}/io/async/test/MockAsyncTransport.h
${FOLLY_DIR}/io/async/test/MockAsyncUDPSocket.h
${FOLLY_DIR}/io/async/test/MockTimeoutManager.h
${FOLLY_DIR}/io/async/test/ScopedBoundPort.h
${FOLLY_DIR}/io/async/test/SocketPair.h
${FOLLY_DIR}/io/async/test/TFOUtil.h
${FOLLY_DIR}/io/async/test/TestSSLServer.h
${FOLLY_DIR}/io/async/test/TimeUtil.h
${FOLLY_DIR}/io/async/test/UndelayedDestruction.h
${FOLLY_DIR}/io/async/test/Util.h
${FOLLY_DIR}/synchronization/test/Semaphore.h
${FOLLY_DIR}/test/DeterministicSchedule.h
${FOLLY_DIR}/test/TestUtils.h
)
# Generate pkg-config variables from folly_deps before we add our own
# build/install-time include directory generator expressions
include(GenPkgConfig)
gen_pkgconfig_vars(FOLLY_PKGCONFIG folly_deps)
target_include_directories(folly_deps
BEFORE
INTERFACE
$
$
)
target_include_directories(folly_deps
INTERFACE
$
)
# =============================================================================
# Build granular libraries from subdirectory CMakeLists.txt files
# Sources are compiled ONCE via OBJECT libraries, then reused for both
# granular .a files and the monolithic libfolly.a
# =============================================================================
# Check for architecture-specific compiler flags needed by subdirectories
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-mpclmul COMPILER_HAS_M_PCLMUL)
# GCC coroutines support
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
check_cxx_compiler_flag(-fcoroutines COMPILER_HAS_F_COROUTINES)
if (COMPILER_HAS_F_COROUTINES)
message(STATUS "GCC has C++ coroutines support, enabling for Folly.")
add_compile_options($<$:-fcoroutines>)
else()
message(STATUS "GCC lacks C++ coroutines, disabling Folly coroutines.")
endif()
endif()
add_subdirectory(folly)
# Create monolithic folly library from all component OBJECT libraries
folly_create_monolithic_library()
# Resolve deferred dependencies (must be after monolithic library is created)
folly_resolve_deferred_dependencies()
# Define FOLLY_CERTS_DIR for the granular test_util target
# This points to the SSL test certificates used by TestSSLServer and related tests
if(TARGET folly_testing_test_util)
target_compile_definitions(folly_testing_test_util INTERFACE
FOLLY_CERTS_DIR="${FOLLY_DIR}/io/async/test/certs"
)
endif()
set(FOLLY_INSTALL_TARGETS folly folly_deps)
# Test utilities exported for use by downstream projects
add_library(folly_test_util
${FOLLY_DIR}/test/DeterministicSchedule.cpp
${FOLLY_DIR}/json/JsonTestUtil.cpp
)
target_compile_definitions(folly_test_util PUBLIC
FOLLY_CERTS_DIR="${FOLLY_DIR}/io/async/test/certs"
)
set_property(TARGET folly_test_util PROPERTY VERSION ${PACKAGE_VERSION})
target_link_libraries(folly_test_util
PUBLIC
folly
${LIBGMOCK_LIBRARIES}
)
apply_folly_compile_options_to_target(folly_test_util)
list(APPEND FOLLY_INSTALL_TARGETS folly_test_util)
install(TARGETS ${FOLLY_INSTALL_TARGETS}
EXPORT folly
RUNTIME DESTINATION bin
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
auto_install_files(folly ${FOLLY_DIR}
${hfiles}
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h
DESTINATION ${INCLUDE_INSTALL_DIR}/folly
COMPONENT dev
)
# Generate the folly-config.cmake file for installation so that
# downstream projects that use on folly can easily depend on it in their CMake
# files using "find_package(folly CONFIG)"
include(CMakePackageConfigHelpers)
configure_package_config_file(
CMake/folly-config.cmake.in
folly-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_DIR}
PATH_VARS
INCLUDE_INSTALL_DIR
CMAKE_INSTALL_DIR
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/folly-config.cmake
DESTINATION ${CMAKE_INSTALL_DIR}
COMPONENT dev
)
install(
EXPORT folly
DESTINATION ${CMAKE_INSTALL_DIR}
NAMESPACE Folly::
FILE folly-targets.cmake
COMPONENT dev
)
# Generate a pkg-config file so that downstream projects that don't use
# CMake can depend on folly using pkg-config.
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/CMake/libfolly.pc.in
${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc.gen
@ONLY
)
# Specify target to allow resolving generator expressions requiring
# a target for CMake >=3.19. See #1414.
# VERSION_GREATER_EQUAL isn't available before CMake 3.7.
if(NOT CMAKE_VERSION VERSION_LESS 3.19)
set(target_arg TARGET folly_deps)
endif()
file(
GENERATE
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc
INPUT ${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc.gen
${target_arg}
)
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/libfolly.pc
DESTINATION ${LIB_INSTALL_DIR}/pkgconfig
COMPONENT dev
)
option(BUILD_TESTS "If enabled, compile the tests." OFF)
option(BUILD_BENCHMARKS "If enabled, compile the benchmarks." OFF)
option(BUILD_BROKEN_TESTS "If enabled, compile tests that are known to be broken." OFF)
option(BUILD_HANGING_TESTS "If enabled, compile tests that are known to hang." OFF)
option(BUILD_SLOW_TESTS "If enabled, compile tests that take a while to run in debug mode." OFF)
if (BUILD_TESTS OR BUILD_BENCHMARKS)
option(USE_CMAKE_GOOGLE_TEST_INTEGRATION "If enabled, use the google test integration included in CMake." ON)
find_package(GMock MODULE REQUIRED)
find_package(GTest MODULE REQUIRED)
if (USE_CMAKE_GOOGLE_TEST_INTEGRATION)
include(GoogleTest OPTIONAL RESULT_VARIABLE HAVE_CMAKE_GTEST)
enable_testing()
else()
set(HAVE_CMAKE_GTEST OFF)
endif()
# The ThreadLocalTest code uses a helper shared library for one of its tests.
# This can only be built if folly itself was built as a shared library.
if (BUILD_SHARED_LIBS)
add_library(thread_local_test_lib MODULE
${FOLLY_DIR}/test/ThreadLocalTestLib.cpp
)
set_target_properties(thread_local_test_lib PROPERTIES PREFIX "")
apply_folly_compile_options_to_target(thread_local_test_lib)
target_link_libraries(thread_local_test_lib PUBLIC folly)
target_include_directories(
thread_local_test_lib
PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
endif()
add_library(folly_test_support
${FOLLY_DIR}/test/common/TestMain.cpp
${FOLLY_DIR}/test/FBVectorTestUtil.cpp
${FOLLY_DIR}/test/DeterministicSchedule.cpp
${FOLLY_DIR}/test/DeterministicSchedule.h
${FOLLY_DIR}/test/SingletonTestStructs.cpp
${FOLLY_DIR}/test/SocketAddressTestHelper.cpp
${FOLLY_DIR}/test/SocketAddressTestHelper.h
${FOLLY_DIR}/compression/test/CodingTestUtils.cpp
${FOLLY_DIR}/container/test/F14TestUtil.h
${FOLLY_DIR}/container/test/TrackingTypes.h
${FOLLY_DIR}/futures/test/TestExecutor.cpp
${FOLLY_DIR}/futures/test/TestExecutor.h
${FOLLY_DIR}/io/async/test/BlockingSocket.h
${FOLLY_DIR}/io/async/test/CallbackStateEnum.h
${FOLLY_DIR}/io/async/test/ConnCallback.h
${FOLLY_DIR}/io/async/test/MockAsyncServerSocket.h
${FOLLY_DIR}/io/async/test/MockAsyncSocket.h
${FOLLY_DIR}/io/async/test/MockAsyncSSLSocket.h
${FOLLY_DIR}/io/async/test/MockAsyncTransport.h
${FOLLY_DIR}/io/async/test/MockAsyncUDPSocket.h
${FOLLY_DIR}/io/async/test/MockTimeoutManager.h
${FOLLY_DIR}/io/async/test/ScopedBoundPort.cpp
${FOLLY_DIR}/io/async/test/ScopedBoundPort.h
${FOLLY_DIR}/io/async/test/SocketPair.cpp
${FOLLY_DIR}/io/async/test/SocketPair.h
${FOLLY_DIR}/io/async/test/SSLUtil.cpp
${FOLLY_DIR}/io/async/test/SSLUtil.h
${FOLLY_DIR}/io/async/test/TFOUtil.cpp
${FOLLY_DIR}/io/async/test/TFOUtil.h
${FOLLY_DIR}/io/async/test/TestSSLServer.cpp
${FOLLY_DIR}/io/async/test/TestSSLServer.h
${FOLLY_DIR}/io/async/test/TimeUtil.cpp
${FOLLY_DIR}/io/async/test/TimeUtil.h
${FOLLY_DIR}/io/async/test/UndelayedDestruction.h
${FOLLY_DIR}/io/async/test/Util.h
${FOLLY_DIR}/logging/test/ConfigHelpers.cpp
${FOLLY_DIR}/logging/test/ConfigHelpers.h
${FOLLY_DIR}/logging/test/TestLogHandler.cpp
${FOLLY_DIR}/logging/test/TestLogHandler.h
)
target_compile_definitions(folly_test_support
PUBLIC
${LIBGMOCK_DEFINES}
)
target_include_directories(folly_test_support
SYSTEM
PUBLIC
${LIBGMOCK_INCLUDE_DIR}
${GTEST_INCLUDE_DIRS}
)
target_link_libraries(folly_test_support
PUBLIC
follybenchmark
folly
${LIBGMOCK_LIBRARIES}
glog::glog
)
apply_folly_compile_options_to_target(folly_test_support)
folly_define_tests(
DIRECTORY algorithm/simd/detail/test/
TEST algorithm_simd_detail_simd_any_of_test SOURCES SimdAnyOfTest.cpp
TEST algorithm_simd_detail_simd_for_each_test SOURCES SimdForEachTest.cpp
TEST algorithm_simd_detail_simd_traits_test SOURCES TraitsTest.cpp
TEST algorithm_simd_detail_unroll_utils_test SOURCES UnrollUtilsTest.cpp
DIRECTORY algorithm/simd/test/
TEST algorithm_simd_contains_test SOURCES ContainsTest.cpp
TEST algorithm_simd_find_first_of_test SOURCES find_first_of_test.cpp
TEST algorithm_simd_find_fixed_test SOURCES FindFixedTest.cpp
TEST algorithm_simd_movemask_test SOURCES MovemaskTest.cpp
DIRECTORY chrono/test/
TEST chrono_conv_test WINDOWS_DISABLED
SOURCES ConvTest.cpp
DIRECTORY cli/test/
TEST cli_args_test SOURCES ArgsTest.cpp
DIRECTORY codec/test/
TEST codec_hex_test SOURCES hex_test.cpp
TEST codec_uuid_test SOURCES UuidTest.cpp
DIRECTORY compression/test/
TEST compression_compression_test SLOW SOURCES CompressionTest.cpp
TEST compression_quotient_multiset_test SOURCES QuotientMultiSetTest.cpp
TEST compression_select64_test SOURCES Select64Test.cpp
DIRECTORY compression/elias_fano/test/
TEST compression_alias_fano_bit_vector_coding_test
SOURCES BitVectorCodingTest.cpp
TEST compression_alias_fano_elias_fano_test
SOURCES EliasFanoCodingTest.cpp
DIRECTORY container/test/
TEST container_access_test SOURCES AccessTest.cpp
TEST container_array_test SOURCES ArrayTest.cpp
BENCHMARK container_bit_iterator_bench SOURCES BitIteratorBench.cpp
TEST container_bit_iterator_test SOURCES BitIteratorTest.cpp
TEST container_enumerate_test SOURCES EnumerateTest.cpp
BENCHMARK container_evicting_cache_map_bench
SOURCES EvictingCacheMapBench.cpp
TEST container_evicting_cache_map_test SOURCES EvictingCacheMapTest.cpp
TEST container_f14_fwd_test SOURCES F14FwdTest.cpp
TEST container_f14_map_test SOURCES F14MapTest.cpp
TEST container_f14_set_test SOURCES F14SetTest.cpp
BENCHMARK container_fbvector_benchmark
SOURCES FBVectorBenchmark.cpp
HEADERS FBVectorBenchmarks.cpp.h
TEST container_fbvector_test SOURCES FBVectorTest.cpp
BENCHMARK container_foreach_benchmark SOURCES ForeachBenchmark.cpp
TEST container_foreach_test SOURCES ForeachTest.cpp
BENCHMARK container_hash_maps_bench SOURCES HashMapsBench.cpp
TEST container_heap_vector_types_test SOURCES heap_vector_types_test.cpp
TEST container_map_util_test WINDOWS_DISABLED SOURCES MapUtilTest.cpp
TEST container_merge_test SOURCES MergeTest.cpp
TEST container_regex_match_cache_test SOURCES RegexMatchCacheTest.cpp
TEST container_small_vector_test WINDOWS_DISABLED
SOURCES small_vector_test.cpp
TEST container_sorted_vector_types_test SOURCES sorted_vector_test.cpp
TEST container_span_test SOURCES span_test.cpp
TEST container_std_bitset_test SOURCES StdBitsetTest.cpp
BENCHMARK container_sparse_byte_set_benchmark
SOURCES SparseByteSetBenchmark.cpp
TEST container_sparse_byte_set_test SOURCES SparseByteSetTest.cpp
TEST container_util_test SOURCES UtilTest.cpp
DIRECTORY concurrency/container/test/
TEST concurrency_container_lock_free_ring_buffer_test
SOURCES LockFreeRingBufferTest.cpp
DIRECTORY concurrency/test/
TEST concurrency_atomic_shared_ptr_test SOURCES AtomicSharedPtrTest.cpp
TEST concurrency_cache_locality_test WINDOWS_DISABLED
SOURCES CacheLocalityTest.cpp
TEST concurrency_core_cached_shared_ptr_test
SOURCES CoreCachedSharedPtrTest.cpp
BENCHMARK concurrency_concurrent_hash_map_bench WINDOWS_DISABLED
SOURCES ConcurrentHashMapBench.cpp
TEST concurrency_concurrent_hash_map_stress_test SLOW WINDOWS_DISABLED
SOURCES ConcurrentHashMapStressTest.cpp
TEST concurrency_concurrent_hash_map_test WINDOWS_DISABLED
SOURCES ConcurrentHashMapTest.cpp
TEST concurrency_dynamic_bounded_queue_test WINDOWS_DISABLED
SOURCES DynamicBoundedQueueTest.cpp
TEST concurrency_priority_unbounded_queue_set_test
SOURCES PriorityUnboundedQueueSetTest.cpp
BENCHMARK concurrency_thread_cached_synchronized_bench
SOURCES ThreadCachedSynchronizedBench.cpp
TEST concurrency_thread_cached_synchronized_test
SOURCES ThreadCachedSynchronizedTest.cpp
TEST concurrency_unbounded_queue_test SOURCES UnboundedQueueTest.cpp
DIRECTORY detail/test/
TEST detail_simple_simd_string_utils_test
SOURCES SimpleSimdStringUtilsTest.cpp
TEST detail_split_string_simd_test WINDOWS_DISABLED
SOURCES SplitStringSimdTest.cpp
TEST detail_static_singleton_manager_test
SOURCES StaticSingletonManagerTest.cpp
TEST detail_type_list_test SOURCES TypeListTest.cpp
DIRECTORY detail/base64_detail/test/
TEST detail_base64_detail_test WINDOWS_DISABLED
SOURCES
Base64AgainstScalarTest.cpp
Base64PlatformTest.cpp
Base64SpecialCasesTest.cpp
DIRECTORY executors/test/
TEST executors_async_helpers_test SOURCES AsyncTest.cpp
TEST executors_codel_test WINDOWS_DISABLED SOURCES CodelTest.cpp
BENCHMARK executors_edf_thread_pool_executor_benchmark
SOURCES EDFThreadPoolExecutorBenchmark.cpp
TEST executors_executor_test SOURCES ExecutorTest.cpp
TEST executors_fiber_io_executor_test SOURCES FiberIOExecutorTest.cpp
# FunctionSchedulerTest has a lot of timing-dependent checks,
# and tends to fail on heavily loaded systems.
TEST executors_function_scheduler_test BROKEN
SOURCES FunctionSchedulerTest.cpp
TEST executors_global_executor_test SOURCES GlobalExecutorTest.cpp
TEST executors_serial_executor_test SOURCES SerialExecutorTest.cpp
# Fails in ThreadPoolExecutorTest.RequestContext:719 data2 != nullptr
TEST executors_thread_pool_executor_test BROKEN WINDOWS_DISABLED
SOURCES ThreadPoolExecutorTest.cpp
TEST executors_threaded_executor_test SOURCES ThreadedExecutorTest.cpp
TEST executors_timed_drivable_executor_test
SOURCES TimedDrivableExecutorTest.cpp
DIRECTORY executors/task_queue/test/
TEST executors_task_queue_priority_unbounded_blocking_queue_test
SOURCES PriorityUnboundedBlockingQueueTest.cpp
BENCHMARK executors_task_queue_unbounded_blocking_queue_bench
SOURCES UnboundedBlockingQueueBench.cpp
TEST executors_task_queue_unbounded_blocking_queue_test
SOURCES UnboundedBlockingQueueTest.cpp
#DIRECTORY experimental/test/
#TEST nested_command_line_app_test SOURCES NestedCommandLineAppTest.cpp
#TEST program_options_test SOURCES ProgramOptionsTest.cpp
# Depends on liburcu
#TEST read_mostly_shared_ptr_test SOURCES ReadMostlySharedPtrTest.cpp
#TEST ref_count_test SOURCES RefCountTest.cpp
#DIRECTORY experimental/io/test/
DIRECTORY external/farmhash/test/
TEST external_farmhash_farmhash_test SOURCES farmhash_test.cpp
DIRECTORY logging/test/
TEST logging_async_file_writer_test WINDOWS_DISABLED
SOURCES AsyncFileWriterTest.cpp
TEST logging_autotimer_test SOURCES AutoTimerTest.cpp
TEST logging_config_parser_test SOURCES ConfigParserTest.cpp
TEST logging_config_update_test SOURCES ConfigUpdateTest.cpp
TEST logging_file_handler_factory_test WINDOWS_DISABLED
SOURCES FileHandlerFactoryTest.cpp
TEST logging_glog_formatter_test SOURCES GlogFormatterTest.cpp
TEST logging_immediate_file_writer_test
SOURCES ImmediateFileWriterTest.cpp
TEST logging_log_category_test SOURCES LogCategoryTest.cpp
TEST logging_logger_db_test SOURCES LoggerDBTest.cpp
TEST logging_logger_test WINDOWS_DISABLED SOURCES LoggerTest.cpp
TEST logging_log_level_test SOURCES LogLevelTest.cpp
TEST logging_log_message_test SOURCES LogMessageTest.cpp
TEST logging_log_name_test SOURCES LogNameTest.cpp
TEST logging_log_stream_test SOURCES LogStreamTest.cpp
TEST logging_rate_limiter_test SOURCES RateLimiterTest.cpp
TEST logging_standard_log_handler_test SOURCES StandardLogHandlerTest.cpp
TEST logging_xlog_test WINDOWS_DISABLED
HEADERS
XlogHeader1.h
XlogHeader2.h
SOURCES
XlogFile1.cpp
XlogFile2.cpp
XlogTest.cpp
DIRECTORY fibers/test/
# FiberManager swapWithException fails with segfault
BENCHMARK fibers_fibers_benchmark WINDOWS_DISABLED
SOURCES FibersBenchmark.cpp
TEST fibers_fibers_test BROKEN SOURCES FibersTest.cpp
DIRECTORY functional/test/
TEST functional_apply_tuple_test WINDOWS_DISABLED
SOURCES ApplyTupleTest.cpp
TEST functional_invoke_test WINDOWS_DISABLED
SOURCES InvokeTest.cpp
TEST functional_partial_test SOURCES PartialTest.cpp
TEST functional_protocol_test SOURCES protocol_test.cpp
TEST functional_traits_test SOURCES traits_test.cpp
DIRECTORY futures/test/
TEST futures_barrier_test SOURCES BarrierTest.cpp
TEST futures_callback_lifetime_test SOURCES CallbackLifetimeTest.cpp
TEST futures_collect_test SOURCES CollectTest.cpp
TEST futures_context_test SOURCES ContextTest.cpp
TEST futures_core_test SOURCES CoreTest.cpp
TEST futures_ensure_test SOURCES EnsureTest.cpp
TEST futures_filter_test SOURCES FilterTest.cpp
TEST futures_future_splitter_test SOURCES FutureSplitterTest.cpp
BENCHMARK futures_benchmark WINDOWS_DISABLED
SOURCES Benchmark.cpp
TEST futures_future_test WINDOWS_DISABLED
SOURCES FutureTest.cpp
TEST futures_header_compile_test SOURCES HeaderCompileTest.cpp
TEST futures_interrupt_test SOURCES InterruptTest.cpp
TEST futures_map_test SOURCES MapTest.cpp
TEST futures_non_copyable_lambda_test SOURCES NonCopyableLambdaTest.cpp
TEST futures_poll_test SOURCES PollTest.cpp
TEST futures_promise_test SOURCES PromiseTest.cpp
TEST futures_reduce_test SOURCES ReduceTest.cpp
TEST futures_retrying_test SOURCES RetryingTest.cpp
TEST futures_self_destruct_test SOURCES SelfDestructTest.cpp
TEST futures_shared_promise_test SOURCES SharedPromiseTest.cpp
TEST futures_test_executor_test SOURCES TestExecutorTest.cpp
TEST futures_then_compile_test
HEADERS
ThenCompileTest.h
SOURCES
ThenCompileTest.cpp
TEST futures_then_test SOURCES ThenTest.cpp
TEST futures_timekeeper_test WINDOWS_DISABLED SOURCES TimekeeperTest.cpp
TEST futures_times_test SOURCES TimesTest.cpp
TEST futures_unwrap_test SOURCES UnwrapTest.cpp
TEST futures_via_test SOURCES ViaTest.cpp
TEST futures_wait_test SOURCES WaitTest.cpp
TEST futures_when_test SOURCES WhenTest.cpp
TEST futures_while_do_test SOURCES WhileDoTest.cpp
TEST futures_will_equal_test SOURCES WillEqualTest.cpp
TEST futures_window_test WINDOWS_DISABLED
SOURCES WindowTest.cpp
DIRECTORY gen/test/
# MSVC bug can't resolve initializer_list constructor properly
TEST gen_base_test WINDOWS_DISABLED SOURCES BaseTest.cpp
TEST gen_combine_test SOURCES CombineTest.cpp
BENCHMARK gen_parallel_map_benchmark SOURCES ParallelMapBenchmark.cpp
TEST gen_parallel_map_test SOURCES ParallelMapTest.cpp
BENCHMARK gen_parallel_benchmark SOURCES ParallelBenchmark.cpp
TEST gen_parallel_test SOURCES ParallelTest.cpp
DIRECTORY hash/test/
BENCHMARK hash_checksum_benchmark SOURCES ChecksumBenchmark.cpp
# builds, but tests fail on MSVC cmake build
TEST hash_checksum_test WINDOWS_DISABLED
SOURCES ChecksumTest.cpp
TEST hash_farm_hash_test SOURCES FarmHashTest.cpp
BENCHMARK hash_hash_benchmark WINDOWS_DISABLED
SOURCES HashBenchmark.cpp
TEST hash_hash_test WINDOWS_DISABLED
SOURCES HashTest.cpp
TEST hash_spooky_hash_v1_test SOURCES SpookyHashV1Test.cpp
TEST hash_spooky_hash_v2_test SOURCES SpookyHashV2Test.cpp
TEST hash_traits_test SOURCES traits_test.cpp
TEST hash_unique_hash_key_test SOURCES UniqueHashKeyTest.cpp
DIRECTORY io/test/
TEST io_fs_util_test SOURCES FsUtilTest.cpp
BENCHMARK io_iobuf_benchmark WINDOWS_DISABLED SOURCES IOBufBenchmark.cpp
TEST io_iobuf_test WINDOWS_DISABLED SOURCES IOBufTest.cpp
TEST io_iobuf_cursor_test SOURCES IOBufCursorTest.cpp
TEST io_iobuf_queue_test SOURCES IOBufQueueTest.cpp
TEST io_record_io_test WINDOWS_DISABLED SOURCES RecordIOTest.cpp
TEST io_shutdown_socket_set_test HANGING
SOURCES ShutdownSocketSetTest.cpp
TEST io_socket_option_value_test HANGING
SOURCES SocketOptionValueTest.cpp
DIRECTORY io/async/test/
# A number of tests in the async_test binary are unfortunately flaky.
# When run under Travis CI a number of the tests also hang (it looks
# like they do not get expected socket accept events, causing them
# to never break out of their event loops).
TEST io_async_async_test BROKEN
CONTENT_DIR certs/
HEADERS
AsyncSocketTest.h
AsyncSSLSocketTest.h
SOURCES
AsyncPipeTest.cpp
AsyncSocketExceptionTest.cpp
AsyncSocketTest.cpp
AsyncSocketTest2.cpp
AsyncSSLSocketTest.cpp
AsyncSSLSocketTest2.cpp
AsyncSSLSocketWriteTest.cpp
AsyncTransportTest.cpp
# This is disabled because it depends on things that don't exist
# on Windows.
# TODO: Refactor EventHandlerTest to not use eventfd so it can work on Mac OS X.
#TEST io_async_event_handler_test WINDOWS_DISABLED SOURCES EventHandlerTest.cpp
TEST io_async_async_timeout_test SOURCES AsyncTimeoutTest.cpp
TEST io_async_async_udp_socket_test APPLE_DISABLED WINDOWS_DISABLED
SOURCES AsyncUDPSocketTest.cpp
TEST io_async_delayed_destruction_test SOURCES DelayedDestructionTest.cpp
TEST io_async_delayed_destruction_base_test
SOURCES DelayedDestructionBaseTest.cpp
TEST io_async_destructor_check_test SOURCES DestructorCheckTest.cpp
# Fails with gtest macro error
BENCHMARK io_async_event_base_benchmark SOURCES EventBaseBenchmark.cpp
TEST io_async_event_base_test BROKEN SOURCES EventBaseTest.cpp
TEST io_async_event_base_local_test WINDOWS_DISABLED
SOURCES EventBaseLocalTest.cpp
TEST io_async_hh_wheel_timer_test SOURCES HHWheelTimerTest.cpp
TEST io_async_hh_wheel_timer_slow_tests SLOW
SOURCES HHWheelTimerSlowTests.cpp
TEST io_async_notification_queue_test WINDOWS_DISABLED
SOURCES NotificationQueueTest.cpp
BENCHMARK io_async_request_context_benchmark WINDOWS_DISABLED
SOURCES RequestContextBenchmark.cpp
HEADERS RequestContextHelper.h
TEST io_async_request_context_test WINDOWS_DISABLED
SOURCES RequestContextTest.cpp
TEST io_async_scoped_event_base_thread_test WINDOWS_DISABLED
SOURCES ScopedEventBaseThreadTest.cpp
TEST io_async_ssl_session_test
CONTENT_DIR certs/
SOURCES SSLSessionTest.cpp
TEST io_async_write_chain_async_transport_wrapper_test
SOURCES WriteChainAsyncTransportWrapperTest.cpp
DIRECTORY io/async/ssl/test/
TEST io_async_ssl_ssl_errors_test SOURCES SSLErrorsTest.cpp
DIRECTORY lang/test/
TEST lang_align_test SOURCES AlignTest.cpp
TEST lang_aligned_test SOURCES AlignedTest.cpp
TEST lang_badge_test SOURCES BadgeTest.cpp
TEST lang_bits_class_test SOURCES BitsClassTest.cpp
TEST lang_bits_test SOURCES BitsTest.cpp
TEST lang_c_string_test SOURCES CStringTest.cpp
TEST lang_cast_test SOURCES CastTest.cpp
TEST lang_checked_math_test SOURCES CheckedMathTest.cpp
TEST lang_exception_test SOURCES ExceptionTest.cpp
TEST lang_extern_test SOURCES ExternTest.cpp
TEST lang_ordering_test SOURCES OrderingTest.cpp
TEST lang_pretty_test SOURCES PrettyTest.cpp
TEST lang_propagate_const_test SOURCES PropagateConstTest.cpp
TEST lang_r_value_reference_wrapper_test WINDOWS_DISABLED
SOURCES RValueReferenceWrapperTest.cpp
TEST lang_safe_assert_test SOURCES SafeAssertTest.cpp
TEST lang_switch_test SOURCES SwitchTest.cpp
BENCHMARK lang_to_ascii_benchmark SOURCES ToAsciiBench.cpp
TEST lang_to_ascii_test SOURCES ToAsciiTest.cpp
TEST lang_type_info_test SOURCES TypeInfoTest.cpp
DIRECTORY memory/test/
TEST memory_arena_test WINDOWS_DISABLED SOURCES ArenaTest.cpp
TEST memory_reentrant_allocator_test WINDOWS_DISABLED
SOURCES ReentrantAllocatorTest.cpp
TEST memory_shared_from_this_ptr_test
SOURCES shared_from_this_ptr_test.cpp
TEST memory_thread_cached_arena_test WINDOWS_DISABLED
SOURCES ThreadCachedArenaTest.cpp
TEST memory_mallctl_helper_test SOURCES MallctlHelperTest.cpp
TEST memory_uninitialized_memory_hacks_test
SOURCES UninitializedMemoryHacksTest.cpp
DIRECTORY net/detail/test/
TEST net_detail_socket_file_descriptor_map_test
SOURCES SocketFileDescriptorMapTest.cpp
DIRECTORY portability/test/
TEST portability_constexpr_test SOURCES ConstexprTest.cpp
TEST portability_filesystem_test SOURCES FilesystemTest.cpp
TEST portability_libgen_test SOURCES LibgenTest.cpp
TEST portability_openssl_portability_test
SOURCES OpenSSLPortabilityTest.cpp
TEST portability_pthread_test SOURCES PThreadTest.cpp
TEST portability_time_test WINDOWS_DISABLED
SOURCES TimeTest.cpp
DIRECTORY ssl/test/
TEST ssl_openssl_hash_test SOURCES OpenSSLHashTest.cpp
DIRECTORY stats/test/
TEST stats_buffered_stat_test SOURCES BufferedStatTest.cpp
BENCHMARK stats_digest_builder_benchmark
SOURCES DigestBuilderBenchmark.cpp
TEST stats_digest_builder_test SOURCES DigestBuilderTest.cpp
BENCHMARK stats_histogram_benchmark SOURCES HistogramBenchmark.cpp
TEST stats_histogram_test SOURCES HistogramTest.cpp
BENCHMARK stats_quantile_histogram_benchmark
SOURCES QuantileHistogramBenchmark.cpp
TEST stats_quantile_estimator_test SOURCES QuantileEstimatorTest.cpp
TEST stats_sliding_window_test SOURCES SlidingWindowTest.cpp
BENCHMARK stats_tdigest_benchmark SOURCES TDigestBenchmark.cpp
TEST stats_tdigest_test SOURCES TDigestTest.cpp
TEST stats_timeseries_histogram_test SOURCES TimeseriesHistogramTest.cpp
TEST stats_timeseries_test SOURCES TimeSeriesTest.cpp
DIRECTORY synchronization/test/
TEST synchronization_atomic_util_test SOURCES AtomicUtilTest.cpp
TEST synchronization_atomic_struct_test SOURCES AtomicStructTest.cpp
BENCHMARK synchronization_baton_benchmark SOURCES BatonBenchmark.cpp
TEST synchronization_baton_test SOURCES BatonTest.cpp
TEST synchronization_call_once_test SOURCES CallOnceTest.cpp
TEST synchronization_event_count_test SOURCES EventCountTest.cpp
BENCHMARK synchronization_hazptr_bench SOURCES HazptrBench.cpp
TEST synchronization_hazptr_test SOURCES HazptrTest.cpp
BENCHMARK synchronization_lifo_sem_bench WINDOWS_DISABLED
SOURCES LifoSemBench.cpp
TEST synchronization_lifo_sem_test WINDOWS_DISABLED
SOURCES LifoSemTests.cpp
TEST synchronization_relaxed_atomic_test WINDOWS_DISABLED
SOURCES RelaxedAtomicTest.cpp
TEST synchronization_rw_spin_lock_test SOURCES RWSpinLockTest.cpp
TEST synchronization_semaphore_test WINDOWS_DISABLED
SOURCES SemaphoreTest.cpp
BENCHMARK synchronization_small_locks_benchmark
SOURCES SmallLocksBenchmark.cpp
TEST synchronization_small_locks_test SOURCES SmallLocksTest.cpp
DIRECTORY synchronization/detail/test/
TEST synchronization_detail_inline_function_ref_test
SOURCES InlineFunctionRefTest.cpp
DIRECTORY system/test/
TEST system_at_fork_test WINDOWS_DISABLED SOURCES AtForkTest.cpp
TEST system_memory_mapping_test SOURCES MemoryMappingTest.cpp
TEST system_shell_test SOURCES ShellTest.cpp
#TEST system_subprocess_test SOURCES SubprocessTest.cpp
TEST system_thread_id_test SOURCES ThreadIdTest.cpp
TEST system_thread_name_test WINDOWS_DISABLED
SOURCES ThreadNameTest.cpp
DIRECTORY test/
TEST ahm_int_stress_test SOURCES AHMIntStressTest.cpp
TEST arena_smartptr_test SOURCES ArenaSmartPtrTest.cpp
BENCHMARK ascii_case_insensitive_benchmark
SOURCES AsciiCaseInsensitiveBenchmark.cpp
TEST ascii_check_test SOURCES AsciiCaseInsensitiveTest.cpp
TEST atomic_hash_array_test SOURCES AtomicHashArrayTest.cpp
TEST atomic_hash_map_test HANGING
SOURCES AtomicHashMapTest.cpp
TEST atomic_linked_list_test SOURCES AtomicLinkedListTest.cpp
TEST atomic_unordered_map_test SOURCES AtomicUnorderedMapTest.cpp
TEST base64_test SOURCES base64_test.cpp
TEST buffered_atomic_test SOURCES BufferedAtomicTest.cpp
TEST cancellation_token_test SOURCES CancellationTokenTest.cpp
TEST chrono_test SOURCES ChronoTest.cpp
TEST clock_gettime_wrappers_test SOURCES ClockGettimeWrappersTest.cpp
TEST concurrent_bit_set_test SOURCES ConcurrentBitSetTest.cpp
BENCHMARK concurrent_skip_list_benchmark
SOURCES ConcurrentSkipListBenchmark.cpp
TEST concurrent_skip_list_test SOURCES ConcurrentSkipListTest.cpp
# Builds but fails test on constexpr_exp_floating std::exp(471.L)
TEST constexpr_math_test WINDOWS_DISABLED
SOURCES ConstexprMathTest.cpp
TEST conv_test SOURCES ConvTest.cpp
TEST cpu_id_test SOURCES CpuIdTest.cpp
TEST demangle_test SOURCES DemangleTest.cpp
TEST deterministic_schedule_test SOURCES DeterministicScheduleTest.cpp
TEST discriminated_ptr_test SOURCES DiscriminatedPtrTest.cpp
TEST endian_test SOURCES EndianTest.cpp
TEST exception_string_test BROKEN SOURCES ExceptionStringTest.cpp
TEST exception_test SOURCES ExceptionTest.cpp
BENCHMARK exception_wrapper_benchmark WINDOWS_DISABLED
SOURCES ExceptionWrapperBenchmark.cpp
TEST exception_wrapper_test WINDOWS_DISABLED
SOURCES ExceptionWrapperTest.cpp
TEST expected_coroutines_test SOURCES ExpectedCoroutinesTest.cpp
TEST expected_test WINDOWS_DISABLED
SOURCES ExpectedTest.cpp
BENCHMARK fbstring_benchmark WINDOWS_DISABLED
SOURCES FBStringBenchmark.cpp
HEADERS FBStringTestBenchmarks.cpp.h
TEST fbstring_test WINDOWS_DISABLED SOURCES FBStringTest.cpp
TEST file_test SOURCES FileTest.cpp
# Open-source linux build can't handle running this.
#TEST file_lock_test SOURCES FileLockTest.cpp
TEST file_util_test SOURCES FileUtilTest.cpp
BENCHMARK fingerprint_benchmark SOURCES FingerprintBenchmark.cpp
TEST fingerprint_test SOURCES FingerprintTest.cpp
TEST fixed_string_test SOURCES FixedStringTest.cpp
TEST fmt_utility_test SOURCES FmtUtilityTest.cpp
TEST format_other_test SOURCES FormatOtherTest.cpp
BENCHMARK format_benchmark SOURCES FormatBenchmark.cpp
TEST format_test SOURCES FormatTest.cpp
TEST function_test BROKEN
SOURCES FunctionTest.cpp
BENCHMARK function_ref_benchmark SOURCES FunctionRefBenchmark.cpp
TEST function_ref_test SOURCES FunctionRefTest.cpp
TEST futex_test SOURCES FutexTest.cpp
TEST glog_test SOURCES GLogTest.cpp
TEST group_varint_test SOURCES GroupVarintTest.cpp
TEST group_varint_test_ssse3 SOURCES GroupVarintTest.cpp
TEST indestructible_test SOURCES IndestructibleTest.cpp
TEST indexed_mem_pool_test BROKEN
SOURCES IndexedMemPoolTest.cpp
TEST iterators_test SOURCES IteratorsTest.cpp
TEST lazy_test SOURCES LazyTest.cpp
TEST locks_test SOURCES SpinLockTest.cpp
TEST math_test SOURCES MathTest.cpp
BENCHMARK memcpy_benchmark WINDOWS_DISABLED SOURCES MemcpyBenchmark.cpp
TEST memcpy_test SOURCES MemcpyTest.cpp
TEST memory_idler_test SOURCES MemoryIdlerTest.cpp
TEST memory_test WINDOWS_DISABLED
SOURCES MemoryTest.cpp
BENCHMARK memset_benchmark WINDOWS_DISABLED SOURCES MemsetBenchmark.cpp
TEST move_wrapper_test SOURCES MoveWrapperTest.cpp
TEST mpmc_pipeline_test SOURCES MPMCPipelineTest.cpp
TEST mpmc_queue_test SLOW
SOURCES MPMCQueueTest.cpp
TEST network_address_test HANGING
SOURCES
IPAddressTest.cpp
MacAddressTest.cpp
SocketAddressTest.cpp
TEST optional_coroutines_test SOURCES OptionalCoroutinesTest.cpp
TEST optional_test SOURCES OptionalTest.cpp
TEST packed_sync_ptr_test HANGING
SOURCES PackedSyncPtrTest.cpp
TEST padded_test SOURCES PaddedTest.cpp
#TEST poly_test SOURCES PolyTest.cpp
TEST portability_test SOURCES PortabilityTest.cpp
# Turns out this benchmark uses Linux only API for setting CPU affinity i.e. cpu_set_t.
BENCHMARK producer_consumer_queue_benchmark APPLE_DISABLED WINDOWS_DISABLED
SOURCES ProducerConsumerQueueBenchmark.cpp
TEST producer_consumer_queue_test SLOW
SOURCES ProducerConsumerQueueTest.cpp
BENCHMARK range_find_benchmark SOURCES RangeFindBenchmark.cpp
BENCHMARK random_benchmark SOURCES RandomBenchmark.cpp
TEST random_test SOURCES RandomTest.cpp
TEST range_test SOURCES RangeTest.cpp
TEST replaceable_test WINDOWS_DISABLED SOURCES ReplaceableTest.cpp
TEST scope_guard_test WINDOWS_DISABLED SOURCES ScopeGuardTest.cpp
# Heavily dependent on drand and srand48
#TEST shared_mutex_test SOURCES SharedMutexTest.cpp
# SingletonTest requires Subprocess
#TEST singleton_test SOURCES SingletonTest.cpp
TEST singleton_double_registration_test BROKEN SOURCES
SingletonDoubleRegistration.cpp
TEST singleton_test_global SOURCES SingletonTestGlobal.cpp
TEST singleton_thread_local_test BROKEN SOURCES
SingletonThreadLocalTest.cpp
SingletonThreadLocalTestOverload.cpp
TEST spin_lock_test SOURCES SpinLockTest.cpp
BENCHMARK string_benchmark WINDOWS_DISABLED SOURCES StringBenchmark.cpp
TEST string_test WINDOWS_DISABLED SOURCES StringTest.cpp
BENCHMARK string_to_float_benchmark SOURCES StringToFloatBenchmark.cpp
BENCHMARK synchronized_benchmark WINDOWS_DISABLED
SOURCES SynchronizedBenchmark.cpp
TEST synchronized_test WINDOWS_DISABLED
SOURCES SynchronizedTest.cpp
TEST thread_cached_int_test WINDOWS_DISABLED
SOURCES ThreadCachedIntTest.cpp
TEST thread_local_test WINDOWS_DISABLED SOURCES ThreadLocalTest.cpp
TEST timeout_queue_test SOURCES TimeoutQueueTest.cpp
TEST token_bucket_test SOURCES TokenBucketTest.cpp
TEST traits_test SOURCES TraitsTest.cpp
TEST try_test WINDOWS_DISABLED SOURCES TryTest.cpp
TEST unicode_test SOURCES UnicodeTest.cpp
TEST unit_test SOURCES UnitTest.cpp
BENCHMARK uri_benchmark SOURCES UriBenchmark.cpp
TEST uri_test SOURCES UriTest.cpp
TEST utf8_string_test SOURCES UTF8StringTest.cpp
TEST utility_test SOURCES UtilityTest.cpp
TEST varint_test SOURCES VarintTest.cpp
DIRECTORY test/function_benchmark/
BENCHMARK function_benchmark
SOURCES main.cpp benchmark_impl.cpp test_functions.cpp
DIRECTORY testing/test/
TEST testing_test_util_test SOURCES TestUtilTest.cpp
DIRECTORY tracing/test/
TEST static_tracepoint_section_test
SOURCES StaticTracepointSectionTest.cpp
DIRECTORY json/test/
TEST json_dynamic_converter_test SOURCES DynamicConverterTest.cpp
TEST json_dynamic_other_test SOURCES DynamicOtherTest.cpp
TEST json_dynamic_parser_test SOURCES DynamicParserTest.cpp
TEST json_dynamic_test SOURCES DynamicTest.cpp
# MSVC Preprocessor stringizing raw string literals bug
TEST json_json_test WINDOWS_DISABLED SOURCES JsonTest.cpp
BENCHMARK json_json_benchmark SOURCES JsonBenchmark.cpp
TEST json_json_other_test SOURCES JsonOtherTest.cpp
TEST json_json_patch_test SOURCES json_patch_test.cpp
TEST json_json_pointer_test SOURCES json_pointer_test.cpp
TEST json_json_schema_test SOURCES JSONSchemaTest.cpp
)
if (${LIBSODIUM_FOUND})
folly_define_tests(
DIRECTORY crypto/test/
TEST crypto_blake2xb_test SOURCES Blake2xbTest.cpp
BENCHMARK crypto_lt_hash_benchmark SOURCES LtHashBenchmark.cpp
TEST crypto_lt_hash_test SOURCES LtHashTest.cpp
)
endif()
if (${LIBAIO_FOUND})
folly_define_tests(
DIRECTORY io/async/test/
TEST io_async_async_io_test
SOURCES
AsyncIOTest.cpp
AsyncBaseTestLib.cpp
IoTestTempFileUtil.cpp
)
endif()
get_target_property(pic folly POSITION_INDEPENDENT_CODE)
if (pic)
add_library(singleton_thread_local_overload
SHARED ${FOLLY_DIR}/test/SingletonThreadLocalTestOverload.cpp)
apply_folly_compile_options_to_target(singleton_thread_local_overload)
set_target_properties(singleton_thread_local_overload PROPERTIES
PREFIX ""
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/folly/test
)
target_link_libraries(singleton_thread_local_overload PRIVATE folly)
folly_define_tests(
DIRECTORY test/
TEST singleton_thread_local_test SOURCES SingletonThreadLocalTest.cpp
)
endif()
endif()
================================================
FILE: CMakeListsForBuck2.txt
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# We use the GoogleTest module if it is available (only in CMake 3.9+)
# It requires CMP0054 and CMP0057 to be enabled.
if (POLICY CMP0054)
cmake_policy(SET CMP0054 NEW)
endif()
if (POLICY CMP0057)
cmake_policy(SET CMP0057 NEW)
endif()
# CMP0075 Include file check macros honor CMAKE_REQUIRED_LIBRARIES
if(POLICY CMP0075)
cmake_policy(SET CMP0075 NEW)
endif()
# includes
set(CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/CMake"
# for in-fbsource builds
"${CMAKE_CURRENT_SOURCE_DIR}/../opensource/fbcode_builder/CMake"
# For shipit-transformed builds
"${CMAKE_CURRENT_SOURCE_DIR}/build/fbcode_builder/CMake"
${CMAKE_MODULE_PATH})
# package information
set(PACKAGE_NAME "folly")
if (NOT DEFINED PACKAGE_VERSION)
set(PACKAGE_VERSION "0.58.0-dev")
endif()
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE_NAME}-${PACKAGE_VERSION}")
set(PACKAGE_BUGREPORT "https://github.com/facebook/folly/issues")
# 150+ tests in the root folder anyone? No? I didn't think so.
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
project(${PACKAGE_NAME} CXX C ASM)
set(INCLUDE_INSTALL_DIR include CACHE STRING
"The subdirectory where header files should be installed")
set(LIB_INSTALL_DIR lib CACHE STRING
"The subdirectory where libraries should be installed")
set(BIN_INSTALL_DIR bin CACHE STRING
"The subdirectory where binaries should be installed")
set(CMAKE_INSTALL_DIR lib/cmake/folly CACHE STRING
"The subdirectory where CMake package config files should be installed")
option(BUILD_SHARED_LIBS
"If enabled, build folly as a shared library. \
This is generally discouraged, since folly does not commit to having \
a stable ABI."
OFF
)
# Mark BUILD_SHARED_LIBS as an "advanced" option, since enabling it
# is generally discouraged.
mark_as_advanced(BUILD_SHARED_LIBS)
set(FOLLY_SUPPORT_SHARED_LIBRARY "${BUILD_SHARED_LIBS}")
include(FBBuildOptions)
fb_activate_static_library_option()
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
message(STATUS "setting C++ standard to C++${CMAKE_CXX_STANDARD}")
endif()
if(NOT DEFINED IS_X86_64_ARCH AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64|AMD64")
set(IS_X86_64_ARCH TRUE)
else()
set(IS_X86_64_ARCH FALSE)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# Check target architecture
if (NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "Folly requires a 64bit target architecture.")
endif()
if (MSVC_VERSION LESS 1900)
message(
FATAL_ERROR
"This build script only supports building Folly on 64-bit Windows with "
"at least Visual Studio 2017. "
"MSVC version '${MSVC_VERSION}' is not supported."
)
endif()
endif()
set(TOP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
set(FOLLY_DIR "${CMAKE_CURRENT_SOURCE_DIR}/folly")
set(
FOLLY_DIR_PREFIXES
"${CMAKE_CURRENT_SOURCE_DIR}:${CMAKE_CURRENT_BINARY_DIR}"
)
include(GNUInstallDirs)
set(CMAKE_THREAD_PREFER_PTHREAD ON)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
set(FOLLY_HAVE_PTHREAD "${CMAKE_USE_PTHREADS_INIT}")
list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads)
list(APPEND FOLLY_LINK_LIBRARIES Threads::Threads)
if(MSVC)
include(FollyCompilerMSVC)
else()
include(FollyCompilerUnix)
endif()
include(FollyFunctions)
include(folly-deps) # Find the required packages
include(FollyConfigChecks)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/CMake/folly-config.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/folly/folly-config.h
)
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at . All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Folly
We want to make contributing to this project as easy and transparent as
possible.
## Code of Conduct
The code of conduct is described in [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
## Pull Requests
We actively welcome your pull requests.
1. Fork the repo and create your branch from `master`.
2. If you've added code that should be tested, add tests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes.
5. If you haven't already, complete the Contributor License Agreement ("CLA").
## Contributor License Agreement ("CLA")
In order to accept your pull request, we need you to submit a CLA. You only need
to do this once to work on any of Facebook's open source projects.
Complete your CLA here:
## Issues
We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue.
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
disclosure of security bugs. In those cases, please go through the process
outlined on that page and do not file a public issue.
## License
By contributing to folly, you agree that your contributions will be licensed
under the LICENSE file in the root directory of this source tree.
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Files in folly/external/farmhash licensed as follows
Copyright (c) 2014 Google, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: PACKAGE
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
load("@shim//:cfg.bzl", "SHIM_ALIASES", "set_cfg_constructor", "get_shim_modifiers")
load("@prelude//cfg/modifier:set_cfg_modifiers.bzl", "set_cfg_modifiers")
# Activate cfg modifiers from CLI / PACKAGE / targets
set_cfg_constructor(SHIM_ALIASES)
modifiers = get_shim_modifiers()
set_cfg_modifiers(modifiers)
================================================
FILE: README.md
================================================
Folly: Facebook Open-source Library
===================================
# What is `folly`?
Folly (an acronym loosely after Facebook Open Source Library) is a
library of C++20 components designed with practicality and efficiency
in mind. **Folly contains a variety of core library components used extensively
at Facebook**. In particular, it's often a dependency of Facebook's other
open source C++ efforts and place where those projects can share code.
It complements (as opposed to competing against) offerings
such as Boost and of course `std`. In fact, we embark on defining our
own component only when something we need is either not available, or
does not meet the needed performance profile. We endeavor to remove
things from folly if or when `std` or Boost obsoletes them.
Performance concerns permeate much of Folly, sometimes leading to
designs that are more idiosyncratic than they would otherwise be (see
e.g. `PackedSyncPtr.h`, `SmallLocks.h`). Good performance at large
scale is a unifying theme in all of Folly.
## Check it out in the intro video
[](https://www.youtube.com/watch?v=Wr_IfOICYSs)
# Logical Design
Folly is a collection of relatively independent components, some as
simple as a few symbols. There is no restriction on internal
dependencies, meaning that a given folly module may use any other
folly components.
All symbols are defined in the top-level namespace `folly`, except of
course macros. Macro names are ALL_UPPERCASE and should be prefixed
with `FOLLY_`. Namespace `folly` defines other internal namespaces
such as `internal` or `detail`. User code should not depend on symbols
in those namespaces.
# Physical Design
At the top level Folly uses the classic "stuttering" scheme
`folly/folly` used by Boost and others. The first directory serves as
an installation root of the library (with possible versioning a la
`folly-1.0/`), and the second is to distinguish the library when
including files, e.g. `#include `.
The directory structure is flat (mimicking the namespace structure),
i.e. we don't have an elaborate directory hierarchy (it is possible
this will change in future versions). The subdirectory `experimental`
contains files that are used inside folly and possibly at Facebook but
not considered stable enough for client use. Your code should not use
files in `folly/experimental` lest it may break when you update Folly.
The `folly/folly/test` subdirectory includes the unittests for all
components, usually named `ComponentXyzTest.cpp` for each
`ComponentXyz.*`. The `folly/folly/docs` directory contains
documentation.
# What's in it?
Because of folly's fairly flat structure, the best way to see what's in it
is to look at the headers in [top level `folly/` directory](https://github.com/facebook/folly/tree/main/folly). You can also
check the [`docs` folder](folly/docs) for documentation, starting with the
[overview](folly/docs/Overview.md).
Folly is published on GitHub at https://github.com/facebook/folly.
# Build Notes
Because folly does not provide any ABI compatibility guarantees from commit to
commit, we generally recommend building folly as a static library.
folly supports gcc (5.1+), clang, or MSVC. It should run on Linux (x86-32,
x86-64, and ARM), iOS, macOS, and Windows (x86-64). The CMake build is only
tested on some of these platforms; at a minimum, we aim to support macOS and
Linux (on the latest Ubuntu LTS release or newer.)
## `getdeps.py`
This script is used by many of Meta's OSS tools. It will download and build all of the necessary dependencies first, and will then invoke cmake etc to build folly. This will help ensure that you build with relevant versions of all of the dependent libraries, taking into account what versions are installed locally on your system.
It's written in python so you'll need python3.6 or later on your PATH. It works on Linux, macOS and Windows.
The settings for folly's cmake build are held in its getdeps manifest `build/fbcode_builder/manifests/folly`, which you can edit locally if desired.
### Dependencies
If on Linux or MacOS (with homebrew installed) you can install system dependencies to save building them:
# Clone the repo
git clone https://github.com/facebook/folly
# Install dependencies
cd folly
sudo ./build/fbcode_builder/getdeps.py install-system-deps --recursive
If you'd like to see the packages before installing them:
./build/fbcode_builder/getdeps.py install-system-deps --dry-run --recursive
On other platforms or if on Linux and without system dependencies `getdeps.py` will mostly download and build them for you during the build step.
Some of the dependencies `getdeps.py` uses and installs are:
* a version of boost compiled with C++14 support.
* googletest is required to build and run folly's tests.
### Build
This script will download and build all of the necessary dependencies first,
and will then invoke cmake etc to build folly. This will help ensure that you build with relevant versions of all of the dependent libraries, taking into account what versions are installed locally on your system.
`getdeps.py` currently requires python 3.6+ to be on your path.
`getdeps.py` will invoke cmake etc.
# Clone the repo
git clone https://github.com/facebook/folly
cd folly
# Build, using system dependencies if available
python3 ./build/fbcode_builder/getdeps.py --allow-system-packages build
It puts output in its scratch area:
* `installed/folly/lib/libfolly.a`: Library
You can also specify a `--scratch-path` argument to control
the location of the scratch directory used for the build. You can find the default scratch install location from logs or with `python3 ./build/fbcode_builder/getdeps.py show-inst-dir`.
There are also
`--install-dir` and `--install-prefix` arguments to provide some more
fine-grained control of the installation directories. However, given that
folly provides no compatibility guarantees between commits we generally
recommend building and installing the libraries to a temporary location, and
then pointing your project's build at this temporary location, rather than
installing folly in the traditional system installation directories. e.g., if you are building with CMake you can use the `CMAKE_PREFIX_PATH` variable to allow CMake to find folly in this temporary installation directory when
building your project.
If you want to invoke `cmake` again to iterate, there is a helpful `run_cmake.py` script output in the scratch build directory. You can find the scratch build directory from logs or with `python3 ./build/fbcode_builder/getdeps.py show-build-dir`.
### Run tests
By default `getdeps.py` will build the tests for folly. To run them:
cd folly
python3 ./build/fbcode_builder/getdeps.py --allow-system-packages test
### `build.sh`/`build.bat` wrapper
`build.sh` can be used on Linux and MacOS, on Windows use
the `build.bat` script instead. Its a wrapper around `getdeps.py`.
## Build with cmake directly
If you don't want to let getdeps invoke cmake for you then by default, building the tests is disabled as part of the CMake `all` target.
To build the tests, specify `-DBUILD_TESTS=ON` to CMake at configure time.
NB if you want to invoke `cmake` again to iterate on a `getdeps.py` build, there is a helpful `run_cmake.py` script output in the scratch-path build directory. You can find the scratch build directory from logs or with `python3 ./build/fbcode_builder/getdeps.py show-build-dir`.
Running tests with ctests also works if you cd to the build dir, e.g.
`(cd $(python3 ./build/fbcode_builder/getdeps.py show-build-dir) && ctest)`
### Finding dependencies in non-default locations
If you have boost, gtest, or other dependencies installed in a non-default
location, you can use the `CMAKE_INCLUDE_PATH` and `CMAKE_LIBRARY_PATH`
variables to make CMAKE look also look for header files and libraries in
non-standard locations. For example, to also search the directories
`/alt/include/path1` and `/alt/include/path2` for header files and the
directories `/alt/lib/path1` and `/alt/lib/path2` for libraries, you can invoke
`cmake` as follows:
```
cmake \
-DCMAKE_INCLUDE_PATH=/alt/include/path1:/alt/include/path2 \
-DCMAKE_LIBRARY_PATH=/alt/lib/path1:/alt/lib/path2 ...
```
## Ubuntu LTS, CentOS Stream, Fedora
Use the `getdeps.py` approach above. We test in CI on Ubuntu LTS, and occasionally on other distros.
If you find the set of system packages is not quite right for your chosen distro, you can specify distro version specific overrides in the dependency manifests (e.g. https://github.com/facebook/folly/blob/main/build/fbcode_builder/manifests/boost ). You could probably make it work on most recent Ubuntu/Debian or Fedora/Redhat derived distributions.
At time of writing (Dec 2021) there is a build break on GCC 11.x based systems in lang_badge_test. If you don't need badge functionality you can work around by commenting it out from CMakeLists.txt (unfortunately fbthrift does need it)
## Windows (Vcpkg)
Note that many tests are disabled for folly Windows builds, you can see them in the log from the cmake configure step, or by looking for WINDOWS_DISABLED in `CMakeLists.txt`
That said, `getdeps.py` builds work on Windows and are tested in CI.
If you prefer, you can try Vcpkg. folly is available in [Vcpkg](https://github.com/Microsoft/vcpkg#vcpkg) and releases may be built via `vcpkg install folly:x64-windows`.
You may also use `vcpkg install folly:x64-windows --head` to build against `main`.
## macOS
`getdeps.py` builds work on macOS and are tested in CI, however if you prefer, you can try one of the macOS package managers
### Homebrew
folly is available as a Formula and releases may be built via `brew install folly`.
You may also use `folly/build/bootstrap-osx-homebrew.sh` to build against `main`:
```
./folly/build/bootstrap-osx-homebrew.sh
```
This will create a build directory `_build` in the top-level.
### MacPorts
Install the required packages from MacPorts:
```
sudo port install \
boost \
cmake \
gflags \
git \
google-glog \
libevent \
libtool \
lz4 \
lzma \
openssl \
snappy \
xz \
zlib
```
Download and install double-conversion:
```
git clone https://github.com/google/double-conversion.git
cd double-conversion
cmake -DBUILD_SHARED_LIBS=ON .
make
sudo make install
```
Download and install folly with the parameters listed below:
```
git clone https://github.com/facebook/folly.git
cd folly
mkdir _build
cd _build
cmake ..
make
sudo make install
```
================================================
FILE: buck2
================================================
#!/usr/bin/env dotslash
{
"name": "buck2",
"platforms": {
"macos-aarch64": {
"size": 31815315,
"hash": "blake3",
"digest": "65a9c2296163b9d0a3e0f6ae36923444c79d5abcc7160e88cdce56d1646e12ca",
"format": "zst",
"path": "buck2-aarch64-apple-darwin",
"providers": [
{
"url": "https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-aarch64-apple-darwin.zst"
}
]
},
"windows-aarch64": {
"size": 27939642,
"hash": "blake3",
"digest": "64bc1eeecdb17aca4cd4cc3e2e878ee5f86d95993c891232add0374bb575304e",
"format": "zst",
"path": "buck2-aarch64-pc-windows-msvc.exe",
"providers": [
{
"url": "https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-aarch64-pc-windows-msvc.exe.zst"
}
]
},
"linux-aarch64": {
"size": 34861909,
"hash": "blake3",
"digest": "c94bc9096171d9c06e478646ce2fc21ab64cf0d9028aa9341de1fec075f54402",
"format": "zst",
"path": "buck2-aarch64-unknown-linux-musl",
"providers": [
{
"url": "https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-aarch64-unknown-linux-musl.zst"
}
]
},
"linux-riscv64": {
"size": 37112472,
"hash": "blake3",
"digest": "0f64fbe707fd43504c353bb633b64e8f6b64884c50ebbde116f197a465d9d04a",
"format": "zst",
"path": "buck2-riscv64gc-unknown-linux-gnu",
"providers": [
{
"url": "https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-riscv64gc-unknown-linux-gnu.zst"
}
]
},
"macos-x86_64": {
"size": 34156976,
"hash": "blake3",
"digest": "a158bd24817ece46ce43e700818704182421ad95a0b2031c2e2a9e6d0fcf29a7",
"format": "zst",
"path": "buck2-x86_64-apple-darwin",
"providers": [
{
"url": "https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-x86_64-apple-darwin.zst"
}
]
},
"windows-x86_64": {
"size": 29670925,
"hash": "blake3",
"digest": "a6522f77c3c9680539df265fc85c8ac98888fa4bb8971e0c43e4b08cc46f3c4a",
"format": "zst",
"path": "buck2-x86_64-pc-windows-msvc.exe",
"providers": [
{
"url": "https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-x86_64-pc-windows-msvc.exe.zst"
}
]
},
"linux-x86_64": {
"size": 36453720,
"hash": "blake3",
"digest": "61bc5de2f2847fef8a4aa80c6d0b29352e1cce667adbe7d07b79c5ced44d7561",
"format": "zst",
"path": "buck2-x86_64-unknown-linux-musl",
"providers": [
{
"url": "https://github.com/facebook/buck2/releases/download/2026-03-15/buck2-x86_64-unknown-linux-musl.zst"
}
]
}
}
}
================================================
FILE: build/buck2/README.md
================================================
# Easy buck2 builds for Facebook projects
This directory contains buck2 targets designed to simplify buck2 builds of
Meta open source projects.
The most notable target is `//build/buck2/install_deps`, which will attempt to
discover and install necessary third party packages from apt / dnf / etc.
See the "repos" directory for the currently supported platforms.
## Deployment
This directory is copied literally into a number of different Facebook open
source repositories. Any change made to code in this directory will be
automatically be replicated by our open source tooling into all GitHub hosted
repositories that use `buck2`. Typically this directory is copied
into the open source repositories as `build/buck2/`.
================================================
FILE: build/buck2/install_deps/BUCK
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the LICENSE file found in the root
# directory of this source tree.
load("@fbcode_macros//build_defs:native_rules.bzl", "buck_sh_binary")
oncall("open_source")
buck_sh_binary(
name = "install_deps",
main = "install_deps.sh",
resources = glob(["repos/*"]),
)
================================================
FILE: build/buck2/install_deps/install_deps.sh
================================================
#!/bin/sh
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the LICENSE file found in the root
# directory of this source tree.
if [ -z "$INSTALL_COMMAND" ]; then
if [ -f /etc/os-release ]; then
. /etc/os-release;
fi
if command -v brew >/dev/null; then
ID="homebrew";
fi
if [ -f "$BUCK_DEFAULT_RUNTIME_RESOURCES/repos/$ID" ]; then
# shellcheck disable=SC1090
. "$BUCK_DEFAULT_RUNTIME_RESOURCES/repos/$ID";
else
echo "Unable to determine platform id / install commands";
return 1;
fi
fi
if [ -z "${BUCK2_COMMAND}" ]; then
if command -v buck2 >/dev/null; then
BUCK2_COMMAND="buck2"
elif command -v dotslash >/dev/null && [ -f ./buck2 ]; then
BUCK2_COMMAND="dotslash ./buck2"
else
echo "Unable to determine buck2 command";
return 1;
fi
fi
__confirm() {
echo "Press \"y\" to continue"
read -r REPLY
expr "X$REPLY" : '^X[Yy]$' >/dev/null
}
PKG_FILE=$(mktemp /tmp/buck2-install-pkgs.XXXXXX)
if ! command -v jq >/dev/null; then
echo "Failed to find jq command, attempting to install with"
echo
echo "$INSTALL_COMMAND" jq
echo
if __confirm; then
eval "$INSTALL_COMMAND jq"
else
echo "Not confirmed, exiting";
exit 1
fi
fi
eval "$BUCK2_COMMAND cquery 'kind(system_packages, deps(//...))' \\
--output-attribute=packages --modifier $ID --json 2>/dev/null \\
| jq -r '.[].packages[]' \\
| sort \\
| uniq \\
> $PKG_FILE"
echo "About to install the project dependencies with the following command:"
echo
eval "cat $PKG_FILE | xargs echo $INSTALL_COMMAND"
echo
if __confirm; then
eval "cat $PKG_FILE | xargs -r $INSTALL_COMMAND"
else
echo "Not installing dependencies"
fi
rm "$PKG_FILE"
================================================
FILE: build/buck2/install_deps/repos/fedora
================================================
INSTALL_COMMAND="sudo -E dnf install -y"
================================================
FILE: build/buck2/install_deps/repos/homebrew
================================================
INSTALL_COMMAND="brew install"
================================================
FILE: build/buck2/install_deps/repos/ubuntu
================================================
INSTALL_COMMAND="sudo -E apt-get install -y"
================================================
FILE: build/fbcode_builder/.gitignore
================================================
# Facebook-internal CI builds don't have write permission outside of the
# source tree, so we install all projects into this directory.
/facebook_ci
__pycache__/
*.pyc
================================================
FILE: build/fbcode_builder/CMake/FBBuildOptions.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
function (fb_activate_static_library_option)
option(USE_STATIC_DEPS_ON_UNIX
"If enabled, use static dependencies on unix systems. This is generally discouraged."
OFF
)
# Mark USE_STATIC_DEPS_ON_UNIX as an "advanced" option, since enabling it
# is generally discouraged.
mark_as_advanced(USE_STATIC_DEPS_ON_UNIX)
if(UNIX AND USE_STATIC_DEPS_ON_UNIX)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a" PARENT_SCOPE)
endif()
option(PREFER_STATIC_DEPS_ON_UNIX
"If enabled, use static dependencies on unix systems as possible as we can. This is generally discouraged."
OFF
)
# Mark PREFER_STATIC_DEPS_ON_UNIX as an "advanced" option, since enabling it
# is generally discouraged.
mark_as_advanced(PREFER_STATIC_DEPS_ON_UNIX)
if(UNIX AND PREFER_STATIC_DEPS_ON_UNIX)
SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a" ".so" PARENT_SCOPE)
endif()
endfunction()
================================================
FILE: build/fbcode_builder/CMake/FBCMakeParseArgs.cmake
================================================
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
# Helper function for parsing arguments to a CMake function.
#
# This function is very similar to CMake's built-in cmake_parse_arguments()
# function, with some improvements:
# - This function correctly handles empty arguments. (cmake_parse_arguments()
# ignores empty arguments.)
# - If a multi-value argument is specified more than once, the subsequent
# arguments are appended to the original list rather than replacing it. e.g.
# if "SOURCES" is a multi-value argument, and the argument list contains
# "SOURCES a b c SOURCES x y z" then the resulting value for SOURCES will be
# "a;b;c;x;y;z" rather than "x;y;z"
# - This function errors out by default on unrecognized arguments. You can
# pass in an extra "ALLOW_UNPARSED_ARGS" argument to make it behave like
# cmake_parse_arguments(), and return the unparsed arguments in a
# _UNPARSED_ARGUMENTS variable instead.
#
# It does look like cmake_parse_arguments() handled empty arguments correctly
# from CMake 3.0 through 3.3, but it seems like this was probably broken when
# it was turned into a built-in function in CMake 3.4. Here is discussion and
# patches that fixed this behavior prior to CMake 3.0:
# https://cmake.org/pipermail/cmake-developers/2013-November/020607.html
#
# The one downside to this function over the built-in cmake_parse_arguments()
# is that I don't think we can achieve the PARSE_ARGV behavior in a non-builtin
# function, so we can't properly handle arguments that contain ";". CMake will
# treat the ";" characters as list element separators, and treat it as multiple
# separate arguments.
#
function(fb_cmake_parse_args PREFIX OPTIONS ONE_VALUE_ARGS MULTI_VALUE_ARGS ARGS)
foreach(option IN LISTS ARGN)
if ("${option}" STREQUAL "ALLOW_UNPARSED_ARGS")
set(ALLOW_UNPARSED_ARGS TRUE)
else()
message(
FATAL_ERROR
"unknown optional argument for fb_cmake_parse_args(): ${option}"
)
endif()
endforeach()
# Define all options as FALSE in the parent scope to start with
foreach(var_name IN LISTS OPTIONS)
set("${PREFIX}_${var_name}" "FALSE" PARENT_SCOPE)
endforeach()
# TODO: We aren't extremely strict about error checking for one-value
# arguments here. e.g., we don't complain if a one-value argument is
# followed by another option/one-value/multi-value name rather than an
# argument. We also don't complain if a one-value argument is the last
# argument and isn't followed by a value.
list(APPEND all_args ${ONE_VALUE_ARGS})
list(APPEND all_args ${MULTI_VALUE_ARGS})
set(current_variable)
set(unparsed_args)
foreach(arg IN LISTS ARGS)
list(FIND OPTIONS "${arg}" opt_index)
if("${opt_index}" EQUAL -1)
list(FIND all_args "${arg}" arg_index)
if("${arg_index}" EQUAL -1)
# This argument does not match an argument name,
# must be an argument value
if("${current_variable}" STREQUAL "")
list(APPEND unparsed_args "${arg}")
else()
# Ugh, CMake lists have a pretty fundamental flaw: they cannot
# distinguish between an empty list and a list with a single empty
# element. We track our own SEEN_VALUES_arg setting to help
# distinguish this and behave properly here.
if ("${SEEN_${current_variable}}" AND "${${current_variable}}" STREQUAL "")
set("${current_variable}" ";${arg}")
else()
list(APPEND "${current_variable}" "${arg}")
endif()
set("SEEN_${current_variable}" TRUE)
endif()
else()
# We found a single- or multi-value argument name
set(current_variable "VALUES_${arg}")
set("SEEN_${arg}" TRUE)
endif()
else()
# We found an option variable
set("${PREFIX}_${arg}" "TRUE" PARENT_SCOPE)
set(current_variable)
endif()
endforeach()
foreach(arg_name IN LISTS ONE_VALUE_ARGS)
if(NOT "${SEEN_${arg_name}}")
unset("${PREFIX}_${arg_name}" PARENT_SCOPE)
elseif(NOT "${SEEN_VALUES_${arg_name}}")
# If the argument was seen but a value wasn't specified, error out.
# We require exactly one value to be specified.
message(
FATAL_ERROR "argument ${arg_name} was specified without a value"
)
else()
list(LENGTH "VALUES_${arg_name}" num_args)
if("${num_args}" EQUAL 0)
# We know an argument was specified and that we called list(APPEND).
# If CMake thinks the list is empty that means there is really a single
# empty element in the list.
set("${PREFIX}_${arg_name}" "" PARENT_SCOPE)
elseif("${num_args}" EQUAL 1)
list(GET "VALUES_${arg_name}" 0 arg_value)
set("${PREFIX}_${arg_name}" "${arg_value}" PARENT_SCOPE)
else()
message(
FATAL_ERROR "too many arguments specified for ${arg_name}: "
"${VALUES_${arg_name}}"
)
endif()
endif()
endforeach()
foreach(arg_name IN LISTS MULTI_VALUE_ARGS)
# If this argument name was never seen, then unset the parent scope
if (NOT "${SEEN_${arg_name}}")
unset("${PREFIX}_${arg_name}" PARENT_SCOPE)
else()
# TODO: Our caller still won't be able to distinguish between an empty
# list and a list with a single empty element. We can tell which is
# which, but CMake lists don't make it easy to show this to our caller.
set("${PREFIX}_${arg_name}" "${VALUES_${arg_name}}" PARENT_SCOPE)
endif()
endforeach()
# By default we fatal out on unparsed arguments, but return them to the
# caller if ALLOW_UNPARSED_ARGS was specified.
if (DEFINED unparsed_args)
if ("${ALLOW_UNPARSED_ARGS}")
set("${PREFIX}_UNPARSED_ARGUMENTS" "${unparsed_args}" PARENT_SCOPE)
else()
message(FATAL_ERROR "unrecognized arguments: ${unparsed_args}")
endif()
endif()
endfunction()
================================================
FILE: build/fbcode_builder/CMake/FBCompilerSettings.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# This file applies common compiler settings that are shared across
# a number of Facebook opensource projects.
# Please use caution and your best judgement before making changes
# to these shared compiler settings in order to avoid accidentally
# breaking a build in another project!
if (WIN32)
include(FBCompilerSettingsMSVC)
else()
include(FBCompilerSettingsUnix)
endif()
================================================
FILE: build/fbcode_builder/CMake/FBCompilerSettingsMSVC.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# This file applies common compiler settings that are shared across
# a number of Facebook opensource projects.
# Please use caution and your best judgement before making changes
# to these shared compiler settings in order to avoid accidentally
# breaking a build in another project!
add_compile_options(
/wd4250 # 'class1' : inherits 'class2::member' via dominance
/Zc:preprocessor # Enable conforming preprocessor for __VA_OPT__ support
)
================================================
FILE: build/fbcode_builder/CMake/FBCompilerSettingsUnix.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# This file applies common compiler settings that are shared across
# a number of Facebook opensource projects.
# Please use caution and your best judgement before making changes
# to these shared compiler settings in order to avoid accidentally
# breaking a build in another project!
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -Wno-deprecated -Wno-deprecated-declarations")
================================================
FILE: build/fbcode_builder/CMake/FBPythonBinary.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
include(FBCMakeParseArgs)
#
# This file contains helper functions for building self-executing Python
# binaries.
#
# This is somewhat different than typical python installation with
# distutils/pip/virtualenv/etc. We primarily want to build a standalone
# executable, isolated from other Python packages on the system. We don't want
# to install files into the standard library python paths. This is more
# similar to PEX (https://github.com/pantsbuild/pex) and XAR
# (https://github.com/facebookincubator/xar). (In the future it would be nice
# to update this code to also support directly generating XAR files if XAR is
# available.)
#
# We also want to be able to easily define "libraries" of python files that can
# be shared and re-used between these standalone python executables, and can be
# shared across projects in different repositories. This means that we do need
# a way to "install" libraries so that they are visible to CMake builds in
# other repositories, without actually installing them in the standard python
# library paths.
#
# If the caller has not already found Python, do so now.
# If we fail to find python now we won't fail immediately, but
# add_fb_python_executable() or add_fb_python_library() will fatal out if they
# are used.
if(NOT TARGET Python3::Interpreter)
# CMake 3.12+ ships with a FindPython3.cmake module. Try using it first.
# We find with QUIET here, since otherwise this generates some noisy warnings
# on versions of CMake before 3.12
if (WIN32)
# On Windows we need both the Interpreter as well as the Development
# libraries.
find_package(Python3 COMPONENTS Interpreter Development QUIET)
else()
find_package(Python3 COMPONENTS Interpreter QUIET)
endif()
if(Python3_Interpreter_FOUND)
message(STATUS "Found Python 3: ${Python3_EXECUTABLE}")
else()
# Try with the FindPythonInterp.cmake module available in older CMake
# versions. Check to see if the caller has already searched for this
# themselves first.
if(NOT PYTHONINTERP_FOUND)
set(Python_ADDITIONAL_VERSIONS 3 3.6 3.5 3.4 3.3 3.2 3.1)
find_package(PythonInterp)
# TODO: On Windows we require the Python libraries as well.
# We currently do not search for them on this code path.
# For now we require building with CMake 3.12+ on Windows, so that the
# FindPython3 code path above is available.
endif()
if(PYTHONINTERP_FOUND)
if("${PYTHON_VERSION_MAJOR}" GREATER_EQUAL 3)
set(Python3_EXECUTABLE "${PYTHON_EXECUTABLE}")
add_custom_target(Python3::Interpreter)
else()
string(
CONCAT FBPY_FIND_PYTHON_ERR
"found Python ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}, "
"but need Python 3"
)
endif()
endif()
endif()
endif()
# Find our helper program.
# We typically install this in the same directory as this .cmake file.
find_program(
FB_MAKE_PYTHON_ARCHIVE "make_fbpy_archive.py"
PATHS ${CMAKE_MODULE_PATH}
)
set(FB_PY_TEST_MAIN "${CMAKE_CURRENT_LIST_DIR}/fb_py_test_main.py")
set(
FB_PY_TEST_DISCOVER_SCRIPT
"${CMAKE_CURRENT_LIST_DIR}/FBPythonTestAddTests.cmake"
)
set(
FB_PY_WIN_MAIN_C
"${CMAKE_CURRENT_LIST_DIR}/fb_py_win_main.c"
)
# An option to control the default installation location for
# install_fb_python_library(). This is relative to ${CMAKE_INSTALL_PREFIX}
set(
FBPY_LIB_INSTALL_DIR "lib/fb-py-libs" CACHE STRING
"The subdirectory where FB python libraries should be installed"
)
#
# Build a self-executing python binary.
#
# This accepts the same arguments as add_fb_python_library().
#
# In addition, a MAIN_MODULE argument is accepted. This argument specifies
# which module should be started as the __main__ module when the executable is
# run. If left unspecified, a __main__.py script must be present in the
# manifest.
#
function(add_fb_python_executable TARGET)
fb_py_check_available()
# Parse the arguments
set(one_value_args BASE_DIR NAMESPACE MAIN_MODULE TYPE)
set(multi_value_args SOURCES DEPENDS)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
fb_py_process_default_args(ARG_NAMESPACE ARG_BASE_DIR)
# Use add_fb_python_library() to perform most of our source handling
add_fb_python_library(
"${TARGET}.main_lib"
BASE_DIR "${ARG_BASE_DIR}"
NAMESPACE "${ARG_NAMESPACE}"
SOURCES ${ARG_SOURCES}
DEPENDS ${ARG_DEPENDS}
)
set(
manifest_files
"$"
)
set(
source_files
"$"
)
# The command to build the executable archive.
#
# If we are using CMake 3.8+ we can use COMMAND_EXPAND_LISTS.
# CMP0067 isn't really the policy we care about, but seems like the best way
# to check if we are running 3.8+.
if (POLICY CMP0067)
set(extra_cmd_params COMMAND_EXPAND_LISTS)
set(make_py_args "${manifest_files}")
else()
set(extra_cmd_params)
set(make_py_args --manifest-separator "::" "$")
endif()
set(output_file "${TARGET}${CMAKE_EXECUTABLE_SUFFIX}")
if(WIN32)
set(zipapp_output "${TARGET}.py_zipapp")
else()
set(zipapp_output "${output_file}")
endif()
set(zipapp_output_file "${zipapp_output}")
set(is_dir_output FALSE)
if(DEFINED ARG_TYPE)
list(APPEND make_py_args "--type" "${ARG_TYPE}")
if ("${ARG_TYPE}" STREQUAL "dir")
set(is_dir_output TRUE)
# CMake doesn't really seem to like having a directory specified as an
# output; specify the __main__.py file as the output instead.
set(zipapp_output_file "${zipapp_output}/__main__.py")
# Update output_file to match zipapp_output_file for dir type
set(output_file "${zipapp_output_file}")
list(APPEND
extra_cmd_params
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${zipapp_output}"
)
endif()
endif()
if(DEFINED ARG_MAIN_MODULE)
list(APPEND make_py_args "--main" "${ARG_MAIN_MODULE}")
endif()
add_custom_command(
OUTPUT "${zipapp_output_file}"
${extra_cmd_params}
COMMAND
"${Python3_EXECUTABLE}" "${FB_MAKE_PYTHON_ARCHIVE}"
-o "${zipapp_output}"
${make_py_args}
DEPENDS
${source_files}
"${TARGET}.main_lib.py_sources_built"
"${FB_MAKE_PYTHON_ARCHIVE}"
)
if(WIN32)
if(is_dir_output)
# TODO: generate a main executable that will invoke Python3
# with the correct main module inside the output directory
else()
add_executable("${TARGET}.winmain" "${FB_PY_WIN_MAIN_C}")
target_link_libraries("${TARGET}.winmain" Python3::Python)
# The Python3::Python target doesn't seem to be set up completely
# correctly on Windows for some reason, and we have to explicitly add
# ${Python3_LIBRARY_DIRS} to the target link directories.
target_link_directories(
"${TARGET}.winmain"
PUBLIC ${Python3_LIBRARY_DIRS}
)
add_custom_command(
OUTPUT "${output_file}"
DEPENDS "${TARGET}.winmain" "${zipapp_output_file}"
COMMAND
"cmd.exe" "/c" "copy" "/b"
"${TARGET}.winmain${CMAKE_EXECUTABLE_SUFFIX}+${zipapp_output}"
"${output_file}"
)
endif()
endif()
# Add an "ALL" target that depends on force ${TARGET},
# so that ${TARGET} will be included in the default list of build targets.
add_custom_target("${TARGET}.GEN_PY_EXE" ALL DEPENDS "${output_file}")
# Allow resolving the executable path for the target that we generate
# via a generator expression like:
# "WATCHMAN_WAIT_PATH=$"
set_property(TARGET "${TARGET}.GEN_PY_EXE"
PROPERTY EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/${output_file}")
endfunction()
# Define a python unittest executable.
# The executable is built using add_fb_python_executable and has the
# following differences:
#
# Each of the source files specified in SOURCES will be imported
# and have unittest discovery performed upon them.
# Those sources will be imported in the top level namespace.
#
# The ENV argument allows specifying a list of "KEY=VALUE"
# pairs that will be used by the test runner to set up the environment
# in the child process prior to running the test. This is useful for
# passing additional configuration to the test.
function(add_fb_python_unittest TARGET)
# Parse the arguments
set(multi_value_args SOURCES DEPENDS ENV PROPERTIES)
set(
one_value_args
WORKING_DIRECTORY BASE_DIR NAMESPACE TEST_LIST DISCOVERY_TIMEOUT TYPE
)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
fb_py_process_default_args(ARG_NAMESPACE ARG_BASE_DIR)
if(NOT ARG_WORKING_DIRECTORY)
# Default the working directory to the current binary directory.
# This matches the default behavior of add_test() and other standard
# test functions like gtest_discover_tests()
set(ARG_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
endif()
if(NOT ARG_TEST_LIST)
set(ARG_TEST_LIST "${TARGET}_TESTS")
endif()
if(NOT ARG_DISCOVERY_TIMEOUT)
set(ARG_DISCOVERY_TIMEOUT 5)
endif()
# Tell our test program the list of modules to scan for tests.
# We scan all modules directly listed in our SOURCES argument, and skip
# modules that came from dependencies in the DEPENDS list.
#
# This is written into a __test_modules__.py module that the test runner
# will look at.
set(
test_modules_path
"${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_test_modules.py"
)
file(WRITE "${test_modules_path}" "TEST_MODULES = [\n")
string(REPLACE "." "/" namespace_dir "${ARG_NAMESPACE}")
if (NOT "${namespace_dir}" STREQUAL "")
set(namespace_dir "${namespace_dir}/")
endif()
set(test_modules)
foreach(src_path IN LISTS ARG_SOURCES)
fb_py_compute_dest_path(
abs_source dest_path
"${src_path}" "${namespace_dir}" "${ARG_BASE_DIR}"
)
string(REPLACE "/" "." module_name "${dest_path}")
string(REGEX REPLACE "\\.py$" "" module_name "${module_name}")
list(APPEND test_modules "${module_name}")
file(APPEND "${test_modules_path}" " '${module_name}',\n")
endforeach()
file(APPEND "${test_modules_path}" "]\n")
# The __main__ is provided by our runner wrapper/bootstrap
list(APPEND ARG_SOURCES "${FB_PY_TEST_MAIN}=__main__.py")
list(APPEND ARG_SOURCES "${test_modules_path}=__test_modules__.py")
if(NOT DEFINED ARG_TYPE)
set(ARG_TYPE "zipapp")
endif()
add_fb_python_executable(
"${TARGET}"
TYPE "${ARG_TYPE}"
NAMESPACE "${ARG_NAMESPACE}"
BASE_DIR "${ARG_BASE_DIR}"
SOURCES ${ARG_SOURCES}
DEPENDS ${ARG_DEPENDS}
)
# Run test discovery after the test executable is built.
# This logic is based on the code for gtest_discover_tests()
set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}")
set(ctest_include_file "${ctest_file_base}_include.cmake")
set(ctest_tests_file "${ctest_file_base}_tests.cmake")
add_custom_command(
TARGET "${TARGET}.GEN_PY_EXE" POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND
"${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_INTERPRETER=${Python3_EXECUTABLE}"
-D "TEST_ENV=${ARG_ENV}"
-D "TEST_EXECUTABLE=$"
-D "TEST_WORKING_DIR=${ARG_WORKING_DIRECTORY}"
-D "TEST_LIST=${ARG_TEST_LIST}"
-D "TEST_PREFIX=${TARGET}::"
-D "TEST_PROPERTIES=${ARG_PROPERTIES}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${FB_PY_TEST_DISCOVER_SCRIPT}"
VERBATIM
)
file(
WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(\"${TARGET}_NOT_BUILT\" \"${TARGET}_NOT_BUILT\")\n"
"endif()\n"
)
set_property(
DIRECTORY APPEND PROPERTY TEST_INCLUDE_FILES
"${ctest_include_file}"
)
endfunction()
#
# Define a python library.
#
# If you want to install a python library generated from this rule note that
# you need to use install_fb_python_library() rather than CMake's built-in
# install() function. This will make it available for other downstream
# projects to use in their add_fb_python_executable() and
# add_fb_python_library() calls. (You do still need to use `install(EXPORT)`
# later to install the CMake exports.)
#
# Parameters:
# - BASE_DIR :
# The base directory path to strip off from each source path. All source
# files must be inside this directory. If not specified it defaults to
# ${CMAKE_CURRENT_SOURCE_DIR}.
# - NAMESPACE :
# The destination namespace where these files should be installed in python
# binaries. If not specified, this defaults to the current relative path of
# ${CMAKE_CURRENT_SOURCE_DIR} inside ${CMAKE_SOURCE_DIR}. e.g., a python
# library defined in the directory repo_root/foo/bar will use a default
# namespace of "foo.bar"
# - SOURCES <...>:
# The python source files.
# You may optionally specify as source using the form: PATH=ALIAS where
# PATH is a relative path in the source tree and ALIAS is the relative
# path into which PATH should be rewritten. This is useful for mapping
# an executable script to the main module in a python executable.
# e.g.: `python/bin/watchman-wait=__main__.py`
# - DEPENDS <...>:
# Other python libraries that this one depends on.
# - INSTALL_DIR :
# The directory where this library should be installed.
# install_fb_python_library() must still be called later to perform the
# installation. If a relative path is given it will be treated relative to
# ${CMAKE_INSTALL_PREFIX}
#
# CMake is unfortunately pretty crappy at being able to define custom build
# rules & behaviors. It doesn't support transitive property propagation
# between custom targets; only the built-in add_executable() and add_library()
# targets support transitive properties.
#
# We hack around this janky CMake behavior by (ab)using interface libraries to
# propagate some of the data we want between targets, without actually
# generating a C library.
#
# add_fb_python_library(SOMELIB) generates the following things:
# - An INTERFACE library rule named SOMELIB.py_lib which tracks some
# information about transitive dependencies:
# - the transitive set of source files in the INTERFACE_SOURCES property
# - the transitive set of manifest files that this library depends on in
# the INTERFACE_INCLUDE_DIRECTORIES property.
# - A custom command that generates a SOMELIB.manifest file.
# This file contains the mapping of source files to desired destination
# locations in executables that depend on this library. This manifest file
# will then be read at build-time in order to build executables.
#
function(add_fb_python_library LIB_NAME)
fb_py_check_available()
# Parse the arguments
# We use fb_cmake_parse_args() rather than cmake_parse_arguments() since
# cmake_parse_arguments() does not handle empty arguments, and it is common
# for callers to want to specify an empty NAMESPACE parameter.
set(one_value_args BASE_DIR NAMESPACE INSTALL_DIR)
set(multi_value_args SOURCES DEPENDS)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
fb_py_process_default_args(ARG_NAMESPACE ARG_BASE_DIR)
string(REPLACE "." "/" namespace_dir "${ARG_NAMESPACE}")
if (NOT "${namespace_dir}" STREQUAL "")
set(namespace_dir "${namespace_dir}/")
endif()
if(NOT DEFINED ARG_INSTALL_DIR)
set(install_dir "${FBPY_LIB_INSTALL_DIR}/")
elseif("${ARG_INSTALL_DIR}" STREQUAL "")
set(install_dir "")
else()
set(install_dir "${ARG_INSTALL_DIR}/")
endif()
# message(STATUS "fb py library ${LIB_NAME}: "
# "NS=${namespace_dir} BASE=${ARG_BASE_DIR}")
# TODO: In the future it would be nice to support pre-compiling the source
# files. We could emit a rule to compile each source file and emit a
# .pyc/.pyo file here, and then have the manifest reference the pyc/pyo
# files.
# Define a library target to help pass around information about the library,
# and propagate dependency information.
#
# CMake make a lot of assumptions that libraries are C++ libraries. To help
# avoid confusion we name our target "${LIB_NAME}.py_lib" rather than just
# "${LIB_NAME}". This helps avoid confusion if callers try to use
# "${LIB_NAME}" on their own as a target name. (e.g., attempting to install
# it directly with install(TARGETS) won't work. Callers must use
# install_fb_python_library() instead.)
add_library("${LIB_NAME}.py_lib" INTERFACE)
# Emit the manifest file.
#
# We write the manifest file to a temporary path first, then copy it with
# configure_file(COPYONLY). This is necessary to get CMake to understand
# that "${manifest_path}" is generated by the CMake configure phase,
# and allow using it as a dependency for add_custom_command().
# (https://gitlab.kitware.com/cmake/cmake/issues/16367)
set(manifest_path "${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}.manifest")
set(tmp_manifest "${manifest_path}.tmp")
file(WRITE "${tmp_manifest}" "FBPY_MANIFEST 1\n")
set(abs_sources)
foreach(src_path IN LISTS ARG_SOURCES)
fb_py_compute_dest_path(
abs_source dest_path
"${src_path}" "${namespace_dir}" "${ARG_BASE_DIR}"
)
list(APPEND abs_sources "${abs_source}")
target_sources(
"${LIB_NAME}.py_lib" INTERFACE
"$"
"$"
)
file(
APPEND "${tmp_manifest}"
"${abs_source} :: ${dest_path}\n"
)
endforeach()
configure_file("${tmp_manifest}" "${manifest_path}" COPYONLY)
target_include_directories(
"${LIB_NAME}.py_lib" INTERFACE
"$"
"$"
)
# Add a target that depends on all of the source files.
# This is needed in case some of the source files are generated. This will
# ensure that these source files are brought up-to-date before we build
# any python binaries that depend on this library.
add_custom_target("${LIB_NAME}.py_sources_built" DEPENDS ${abs_sources})
add_dependencies("${LIB_NAME}.py_lib" "${LIB_NAME}.py_sources_built")
# Hook up library dependencies, and also make the *.py_sources_built target
# depend on the sources for all of our dependencies also being up-to-date.
foreach(dep IN LISTS ARG_DEPENDS)
target_link_libraries("${LIB_NAME}.py_lib" INTERFACE "${dep}.py_lib")
# Mark that our .py_sources_built target depends on each our our dependent
# libraries. This serves two functions:
# - This causes CMake to generate an error message if one of the
# dependencies is never defined. The target_link_libraries() call above
# won't complain if one of the dependencies doesn't exist (since it is
# intended to allow passing in file names for plain library files rather
# than just targets).
# - It ensures that sources for our dependencies are built before any
# executable that depends on us. Note that we depend on "${dep}.py_lib"
# rather than "${dep}.py_sources_built" for this purpose because the
# ".py_sources_built" target won't be available for imported targets.
add_dependencies("${LIB_NAME}.py_sources_built" "${dep}.py_lib")
endforeach()
# Add a custom command to help with library installation, in case
# install_fb_python_library() is called later for this library.
# add_custom_command() only works with file dependencies defined in the same
# CMakeLists.txt file, so we want to make sure this is defined here, rather
# then where install_fb_python_library() is called.
# This command won't be run by default, but will only be run if it is needed
# by a subsequent install_fb_python_library() call.
#
# This command copies the library contents into the build directory.
# It would be nicer if we could skip this intermediate copy, and just run
# make_fbpy_archive.py at install time to copy them directly to the desired
# installation directory. Unfortunately this is difficult to do, and seems
# to interfere with some of the CMake code that wants to generate a manifest
# of installed files.
set(build_install_dir "${CMAKE_CURRENT_BINARY_DIR}/${LIB_NAME}.lib_install")
add_custom_command(
OUTPUT
"${build_install_dir}/${LIB_NAME}.manifest"
COMMAND "${CMAKE_COMMAND}" -E remove_directory "${build_install_dir}"
COMMAND
"${Python3_EXECUTABLE}" "${FB_MAKE_PYTHON_ARCHIVE}" --type lib-install
--install-dir "${LIB_NAME}"
-o "${build_install_dir}/${LIB_NAME}" "${manifest_path}"
DEPENDS
"${abs_sources}"
"${manifest_path}"
"${FB_MAKE_PYTHON_ARCHIVE}"
)
add_custom_target(
"${LIB_NAME}.py_lib_install"
DEPENDS "${build_install_dir}/${LIB_NAME}.manifest"
)
# Set some properties to pass through the install paths to
# install_fb_python_library()
#
# Passing through ${build_install_dir} allows install_fb_python_library()
# to work even if used from a different CMakeLists.txt file than where
# add_fb_python_library() was called (i.e. such that
# ${CMAKE_CURRENT_BINARY_DIR} is different between the two calls).
set(abs_install_dir "${install_dir}")
if(NOT IS_ABSOLUTE "${abs_install_dir}")
set(abs_install_dir "${CMAKE_INSTALL_PREFIX}/${abs_install_dir}")
endif()
string(REGEX REPLACE "/$" "" abs_install_dir "${abs_install_dir}")
set_target_properties(
"${LIB_NAME}.py_lib_install"
PROPERTIES
INSTALL_DIR "${abs_install_dir}"
BUILD_INSTALL_DIR "${build_install_dir}"
)
endfunction()
#
# Install an FB-style packaged python binary.
#
# - DESTINATION :
# Associate the installed target files with the given export-name.
#
function(install_fb_python_executable TARGET)
# Parse the arguments
set(one_value_args DESTINATION)
set(multi_value_args)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
if(NOT DEFINED ARG_DESTINATION)
set(ARG_DESTINATION bin)
endif()
install(
PROGRAMS "$"
DESTINATION "${ARG_DESTINATION}"
)
endfunction()
#
# Install a python library.
#
# - EXPORT :
# Associate the installed target files with the given export-name.
#
# Note that unlike the built-in CMake install() function we do not accept a
# DESTINATION parameter. Instead, use the INSTALL_DIR parameter to
# add_fb_python_library() to set the installation location.
#
function(install_fb_python_library LIB_NAME)
set(one_value_args EXPORT)
fb_cmake_parse_args(ARG "" "${one_value_args}" "" "${ARGN}")
# Export our "${LIB_NAME}.py_lib" target so that it will be available to
# downstream projects in our installed CMake config files.
if(DEFINED ARG_EXPORT)
install(TARGETS "${LIB_NAME}.py_lib" EXPORT "${ARG_EXPORT}")
endif()
# add_fb_python_library() emits a .py_lib_install target that will prepare
# the installation directory. However, it isn't part of the "ALL" target and
# therefore isn't built by default.
#
# Make sure the ALL target depends on it now. We have to do this by
# introducing yet another custom target.
# Add it as a dependency to the ALL target now.
add_custom_target("${LIB_NAME}.py_lib_install_all" ALL)
add_dependencies(
"${LIB_NAME}.py_lib_install_all" "${LIB_NAME}.py_lib_install"
)
# Copy the intermediate install directory generated at build time into
# the desired install location.
get_target_property(dest_dir "${LIB_NAME}.py_lib_install" "INSTALL_DIR")
get_target_property(
build_install_dir "${LIB_NAME}.py_lib_install" "BUILD_INSTALL_DIR"
)
install(
DIRECTORY "${build_install_dir}/${LIB_NAME}"
DESTINATION "${dest_dir}"
)
install(
FILES "${build_install_dir}/${LIB_NAME}.manifest"
DESTINATION "${dest_dir}"
)
endfunction()
# Helper macro to process the BASE_DIR and NAMESPACE arguments for
# add_fb_python_executable() and add_fb_python_executable()
macro(fb_py_process_default_args NAMESPACE_VAR BASE_DIR_VAR)
# If the namespace was not specified, default to the relative path to the
# current directory (starting from the repository root).
if(NOT DEFINED "${NAMESPACE_VAR}")
file(
RELATIVE_PATH "${NAMESPACE_VAR}"
"${CMAKE_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}"
)
endif()
if(NOT DEFINED "${BASE_DIR_VAR}")
# If the base directory was not specified, default to the current directory
set("${BASE_DIR_VAR}" "${CMAKE_CURRENT_SOURCE_DIR}")
else()
# If the base directory was specified, always convert it to an
# absolute path.
get_filename_component("${BASE_DIR_VAR}" "${${BASE_DIR_VAR}}" ABSOLUTE)
endif()
endmacro()
function(fb_py_check_available)
# Make sure that Python 3 and our make_fbpy_archive.py helper script are
# available.
if(NOT Python3_EXECUTABLE)
if(FBPY_FIND_PYTHON_ERR)
message(FATAL_ERROR "Unable to find Python 3: ${FBPY_FIND_PYTHON_ERR}")
else()
message(FATAL_ERROR "Unable to find Python 3")
endif()
endif()
if (NOT FB_MAKE_PYTHON_ARCHIVE)
message(
FATAL_ERROR "unable to find make_fbpy_archive.py helper program (it "
"should be located in the same directory as FBPythonBinary.cmake)"
)
endif()
endfunction()
function(
fb_py_compute_dest_path
src_path_output dest_path_output src_path namespace_dir base_dir
)
if("${src_path}" MATCHES "=")
# We want to split the string on the `=` sign, but cmake doesn't
# provide much in the way of helpers for this, so we rewrite the
# `=` sign to `;` so that we can treat it as a cmake list and
# then index into the components
string(REPLACE "=" ";" src_path_list "${src_path}")
list(GET src_path_list 0 src_path)
# Note that we ignore the `namespace_dir` in the alias case
# in order to allow aliasing a source to the top level `__main__.py`
# filename.
list(GET src_path_list 1 dest_path)
else()
unset(dest_path)
endif()
get_filename_component(abs_source "${src_path}" ABSOLUTE)
if(NOT DEFINED dest_path)
file(RELATIVE_PATH rel_src "${ARG_BASE_DIR}" "${abs_source}")
if("${rel_src}" MATCHES "^../")
message(
FATAL_ERROR "${LIB_NAME}: source file \"${abs_source}\" is not inside "
"the base directory ${ARG_BASE_DIR}"
)
endif()
set(dest_path "${namespace_dir}${rel_src}")
endif()
set("${src_path_output}" "${abs_source}" PARENT_SCOPE)
set("${dest_path_output}" "${dest_path}" PARENT_SCOPE)
endfunction()
================================================
FILE: build/fbcode_builder/CMake/FBPythonTestAddTests.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# Add a command to be emitted to the CTest file
set(ctest_script)
function(add_command CMD)
set(escaped_args "")
foreach(arg ${ARGN})
# Escape all arguments using "Bracket Argument" syntax
# We could skip this for argument that don't contain any special
# characters if we wanted to make the output slightly more human-friendly.
set(escaped_args "${escaped_args} [==[${arg}]==]")
endforeach()
set(ctest_script "${ctest_script}${CMD}(${escaped_args})\n" PARENT_SCOPE)
endfunction()
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR "Test executable does not exist: ${TEST_EXECUTABLE}")
endif()
execute_process(
COMMAND ${CMAKE_COMMAND} -E env ${TEST_ENV} "${TEST_INTERPRETER}" "${TEST_EXECUTABLE}" --list-tests
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
OUTPUT_VARIABLE output
RESULT_VARIABLE result
)
if(NOT "${result}" EQUAL 0)
string(REPLACE "\n" "\n " output "${output}")
message(
FATAL_ERROR
"Error running test executable: ${TEST_EXECUTABLE}\n"
"Output:\n"
" ${output}\n"
)
endif()
# Parse output
string(REPLACE "\n" ";" tests_list "${output}")
foreach(test_name ${tests_list})
add_command(
add_test
"${TEST_PREFIX}${test_name}"
${CMAKE_COMMAND} -E env ${TEST_ENV}
"${TEST_INTERPRETER}" "${TEST_EXECUTABLE}" "${test_name}"
)
add_command(
set_tests_properties
"${TEST_PREFIX}${test_name}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${TEST_PROPERTIES}
)
endforeach()
# Set a list of discovered tests in the parent scope, in case users
# want access to this list as a CMake variable
if(TEST_LIST)
add_command(set ${TEST_LIST} ${tests_list})
endif()
file(WRITE "${CTEST_FILE}" "${ctest_script}")
================================================
FILE: build/fbcode_builder/CMake/FBThriftCppLibrary.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
include(FBCMakeParseArgs)
# Generate a C++ library from a thrift file
#
# Parameters:
# - SERVICES [ ...]
# The names of the services defined in the thrift file.
# - DEPENDS [ ...]
# A list of other thrift C++ libraries that this library depends on.
# - OPTIONS [ ...]
# A list of options to pass to the thrift compiler.
# - INCLUDE_DIR
# The sub-directory where generated headers will be installed.
# Defaults to "include" if not specified. The caller must still call
# install() to install the thrift library if desired.
# - THRIFT_INCLUDE_DIR
# The sub-directory where generated headers will be installed.
# Defaults to "${INCLUDE_DIR}/thrift-files" if not specified.
# The caller must still call install() to install the thrift library if
# desired.
function(add_fbthrift_cpp_library LIB_NAME THRIFT_FILE)
# Parse the arguments
set(one_value_args INCLUDE_DIR THRIFT_INCLUDE_DIR)
set(multi_value_args SERVICES DEPENDS OPTIONS)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
if(NOT DEFINED ARG_INCLUDE_DIR)
set(ARG_INCLUDE_DIR "include")
endif()
if(NOT DEFINED ARG_THRIFT_INCLUDE_DIR)
set(ARG_THRIFT_INCLUDE_DIR "${ARG_INCLUDE_DIR}/thrift-files")
endif()
get_filename_component(base ${THRIFT_FILE} NAME_WE)
get_filename_component(
output_dir
${CMAKE_CURRENT_BINARY_DIR}/${THRIFT_FILE}
DIRECTORY
)
# Generate relative paths in #includes
file(
RELATIVE_PATH include_prefix
"${CMAKE_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}"
)
get_filename_component(include_prefix ${include_prefix} DIRECTORY)
if (NOT "${include_prefix}" STREQUAL "")
list(APPEND ARG_OPTIONS "include_prefix=${include_prefix}")
endif()
# CMake 3.12 is finally getting a list(JOIN) function, but until then
# treating the list as a string and replacing the semicolons is good enough.
string(REPLACE ";" "," GEN_ARG_STR "${ARG_OPTIONS}")
# Compute the list of generated files
list(APPEND generated_headers
"${output_dir}/gen-cpp2/${base}_constants.h"
"${output_dir}/gen-cpp2/${base}_types.h"
"${output_dir}/gen-cpp2/${base}_types.tcc"
"${output_dir}/gen-cpp2/${base}_types_custom_protocol.h"
"${output_dir}/gen-cpp2/${base}_metadata.h"
)
list(APPEND generated_sources
"${output_dir}/gen-cpp2/${base}_constants.cpp"
"${output_dir}/gen-cpp2/${base}_data.h"
"${output_dir}/gen-cpp2/${base}_data.cpp"
"${output_dir}/gen-cpp2/${base}_types.cpp"
"${output_dir}/gen-cpp2/${base}_types_binary.cpp"
"${output_dir}/gen-cpp2/${base}_types_compact.cpp"
"${output_dir}/gen-cpp2/${base}_types_serialization.cpp"
"${output_dir}/gen-cpp2/${base}_metadata.cpp"
)
foreach(service IN LISTS ARG_SERVICES)
list(APPEND generated_headers
"${output_dir}/gen-cpp2/${service}.h"
"${output_dir}/gen-cpp2/${service}.tcc"
"${output_dir}/gen-cpp2/${service}AsyncClient.h"
"${output_dir}/gen-cpp2/${service}_custom_protocol.h"
)
list(APPEND generated_sources
"${output_dir}/gen-cpp2/${service}.cpp"
"${output_dir}/gen-cpp2/${service}AsyncClient.cpp"
"${output_dir}/gen-cpp2/${service}_processmap_binary.cpp"
"${output_dir}/gen-cpp2/${service}_processmap_compact.cpp"
)
endforeach()
# This generator expression gets the list of include directories required
# for all of our dependencies.
# It requires using COMMAND_EXPAND_LISTS in the add_custom_command() call
# below. COMMAND_EXPAND_LISTS is only available in CMake 3.8+
# If we really had to support older versions of CMake we would probably need
# to use a wrapper script around the thrift compiler that could take the
# include list as a single argument and split it up before invoking the
# thrift compiler.
if (NOT POLICY CMP0067)
message(FATAL_ERROR "add_fbthrift_cpp_library() requires CMake 3.8+")
endif()
set(
thrift_include_options
"-I;$,;-I;>"
)
# Emit the rule to run the thrift compiler
add_custom_command(
OUTPUT
${generated_headers}
${generated_sources}
COMMAND_EXPAND_LISTS
COMMAND
"${CMAKE_COMMAND}" -E make_directory "${output_dir}"
COMMAND
"${FBTHRIFT_COMPILER}"
--legacy-strict
--gen "mstch_cpp2:${GEN_ARG_STR}"
"${thrift_include_options}"
-I "${FBTHRIFT_INCLUDE_DIR}"
-o "${output_dir}"
"${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}"
WORKING_DIRECTORY
"${CMAKE_BINARY_DIR}"
MAIN_DEPENDENCY
"${THRIFT_FILE}"
DEPENDS
${ARG_DEPENDS}
"${FBTHRIFT_COMPILER}"
)
# Now emit the library rule to compile the sources
if (BUILD_SHARED_LIBS)
set(LIB_TYPE SHARED)
else ()
set(LIB_TYPE STATIC)
endif ()
add_library(
"${LIB_NAME}" ${LIB_TYPE}
${generated_sources}
)
target_include_directories(
"${LIB_NAME}"
PUBLIC
"$"
"$"
${Xxhash_INCLUDE_DIR}
)
target_link_libraries(
"${LIB_NAME}"
PUBLIC
${ARG_DEPENDS}
FBThrift::thriftcpp2
Folly::folly
mvfst::mvfst_server_async_tran
mvfst::mvfst_server
${Xxhash_LIBRARY}
)
# Add ${generated_headers} to the PUBLIC_HEADER property for ${LIB_NAME}
#
# This allows callers to install it using
# "install(TARGETS ${LIB_NAME} PUBLIC_HEADER)"
# However, note that CMake's PUBLIC_HEADER behavior is rather inflexible,
# and does have any way to preserve header directory structure. Callers
# must be careful to use the correct PUBLIC_HEADER DESTINATION parameter
# when doing this, to put the files the correct directory themselves.
# We define a HEADER_INSTALL_DIR property with the include directory prefix,
# so typically callers should specify the PUBLIC_HEADER DESTINATION as
# "$"
set_property(
TARGET "${LIB_NAME}"
PROPERTY PUBLIC_HEADER ${generated_headers}
)
# Define a dummy interface library to help propagate the thrift include
# directories between dependencies.
add_library("${LIB_NAME}.thrift_includes" INTERFACE)
target_include_directories(
"${LIB_NAME}.thrift_includes"
INTERFACE
"$"
"$"
)
foreach(dep IN LISTS ARG_DEPENDS)
target_link_libraries(
"${LIB_NAME}.thrift_includes"
INTERFACE "${dep}.thrift_includes"
)
endforeach()
set_target_properties(
"${LIB_NAME}"
PROPERTIES
EXPORT_PROPERTIES "THRIFT_INSTALL_DIR"
THRIFT_INSTALL_DIR "${ARG_THRIFT_INCLUDE_DIR}/${include_prefix}"
HEADER_INSTALL_DIR "${ARG_INCLUDE_DIR}/${include_prefix}/gen-cpp2"
)
endfunction()
================================================
FILE: build/fbcode_builder/CMake/FBThriftLibrary.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
include(FBCMakeParseArgs)
include(FBThriftPyLibrary)
include(FBThriftCppLibrary)
#
# add_fbthrift_library()
#
# This is a convenience function that generates thrift libraries for multiple
# languages.
#
# For example:
# add_fbthrift_library(
# foo foo.thrift
# LANGUAGES cpp py
# SERVICES Foo
# DEPENDS bar)
#
# will be expanded into two separate calls:
#
# add_fbthrift_cpp_library(foo_cpp foo.thrift SERVICES Foo DEPENDS bar_cpp)
# add_fbthrift_py_library(foo_py foo.thrift SERVICES Foo DEPENDS bar_py)
#
function(add_fbthrift_library LIB_NAME THRIFT_FILE)
# Parse the arguments
set(one_value_args PY_NAMESPACE INCLUDE_DIR THRIFT_INCLUDE_DIR)
set(multi_value_args SERVICES DEPENDS LANGUAGES CPP_OPTIONS PY_OPTIONS)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
if(NOT DEFINED ARG_INCLUDE_DIR)
set(ARG_INCLUDE_DIR "include")
endif()
if(NOT DEFINED ARG_THRIFT_INCLUDE_DIR)
set(ARG_THRIFT_INCLUDE_DIR "${ARG_INCLUDE_DIR}/thrift-files")
endif()
# CMake 3.12+ adds list(TRANSFORM) which would be nice to use here, but for
# now we still want to support older versions of CMake.
set(CPP_DEPENDS)
set(PY_DEPENDS)
foreach(dep IN LISTS ARG_DEPENDS)
list(APPEND CPP_DEPENDS "${dep}_cpp")
list(APPEND PY_DEPENDS "${dep}_py")
endforeach()
foreach(lang IN LISTS ARG_LANGUAGES)
if ("${lang}" STREQUAL "cpp")
add_fbthrift_cpp_library(
"${LIB_NAME}_cpp" "${THRIFT_FILE}"
SERVICES ${ARG_SERVICES}
DEPENDS ${CPP_DEPENDS}
OPTIONS ${ARG_CPP_OPTIONS}
INCLUDE_DIR "${ARG_INCLUDE_DIR}"
THRIFT_INCLUDE_DIR "${ARG_THRIFT_INCLUDE_DIR}"
)
elseif ("${lang}" STREQUAL "py" OR "${lang}" STREQUAL "python")
if (DEFINED ARG_PY_NAMESPACE)
set(namespace_args NAMESPACE "${ARG_PY_NAMESPACE}")
endif()
add_fbthrift_py_library(
"${LIB_NAME}_py" "${THRIFT_FILE}"
SERVICES ${ARG_SERVICES}
${namespace_args}
DEPENDS ${PY_DEPENDS}
OPTIONS ${ARG_PY_OPTIONS}
THRIFT_INCLUDE_DIR "${ARG_THRIFT_INCLUDE_DIR}"
)
else()
message(
FATAL_ERROR "unknown language for thrift library ${LIB_NAME}: ${lang}"
)
endif()
endforeach()
endfunction()
================================================
FILE: build/fbcode_builder/CMake/FBThriftPyLibrary.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
include(FBCMakeParseArgs)
include(FBPythonBinary)
# Generate a Python library from a thrift file
function(add_fbthrift_py_library LIB_NAME THRIFT_FILE)
# Parse the arguments
set(one_value_args NAMESPACE THRIFT_INCLUDE_DIR)
set(multi_value_args SERVICES DEPENDS OPTIONS)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
if(NOT DEFINED ARG_THRIFT_INCLUDE_DIR)
set(ARG_THRIFT_INCLUDE_DIR "include/thrift-files")
endif()
get_filename_component(base ${THRIFT_FILE} NAME_WE)
set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/${THRIFT_FILE}-py")
# Parse the namespace value
if (NOT DEFINED ARG_NAMESPACE)
set(ARG_NAMESPACE "${base}")
endif()
string(REPLACE "." "/" namespace_dir "${ARG_NAMESPACE}")
set(py_output_dir "${output_dir}/gen-py/${namespace_dir}")
list(APPEND generated_sources
"${py_output_dir}/__init__.py"
"${py_output_dir}/ttypes.py"
"${py_output_dir}/constants.py"
)
foreach(service IN LISTS ARG_SERVICES)
list(APPEND generated_sources
${py_output_dir}/${service}.py
)
endforeach()
# Define a dummy interface library to help propagate the thrift include
# directories between dependencies.
add_library("${LIB_NAME}.thrift_includes" INTERFACE)
target_include_directories(
"${LIB_NAME}.thrift_includes"
INTERFACE
"$"
"$"
)
foreach(dep IN LISTS ARG_DEPENDS)
target_link_libraries(
"${LIB_NAME}.thrift_includes"
INTERFACE "${dep}.thrift_includes"
)
endforeach()
# This generator expression gets the list of include directories required
# for all of our dependencies.
# It requires using COMMAND_EXPAND_LISTS in the add_custom_command() call
# below. COMMAND_EXPAND_LISTS is only available in CMake 3.8+
# If we really had to support older versions of CMake we would probably need
# to use a wrapper script around the thrift compiler that could take the
# include list as a single argument and split it up before invoking the
# thrift compiler.
if (NOT POLICY CMP0067)
message(FATAL_ERROR "add_fbthrift_py_library() requires CMake 3.8+")
endif()
set(
thrift_include_options
"-I;$,;-I;>"
)
# Always force generation of "new-style" python classes for Python 2
list(APPEND ARG_OPTIONS "new_style")
# CMake 3.12 is finally getting a list(JOIN) function, but until then
# treating the list as a string and replacing the semicolons is good enough.
string(REPLACE ";" "," GEN_ARG_STR "${ARG_OPTIONS}")
# Emit the rule to run the thrift compiler
add_custom_command(
OUTPUT
${generated_sources}
COMMAND_EXPAND_LISTS
COMMAND
"${CMAKE_COMMAND}" -E make_directory "${output_dir}"
COMMAND
"${FBTHRIFT_COMPILER}"
--legacy-strict
--gen "py:${GEN_ARG_STR}"
"${thrift_include_options}"
-o "${output_dir}"
"${CMAKE_CURRENT_SOURCE_DIR}/${THRIFT_FILE}"
WORKING_DIRECTORY
"${CMAKE_BINARY_DIR}"
MAIN_DEPENDENCY
"${THRIFT_FILE}"
DEPENDS
"${FBTHRIFT_COMPILER}"
)
# We always want to pass the namespace as "" to this call:
# thrift will already emit the files with the desired namespace prefix under
# gen-py. We don't want add_fb_python_library() to prepend the namespace a
# second time.
add_fb_python_library(
"${LIB_NAME}"
BASE_DIR "${output_dir}/gen-py"
NAMESPACE ""
SOURCES ${generated_sources}
DEPENDS ${ARG_DEPENDS} FBThrift::thrift_py
)
endfunction()
================================================
FILE: build/fbcode_builder/CMake/FindCares.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.
find_path(CARES_INCLUDE_DIR NAMES ares.h)
find_library(CARES_LIBRARIES NAMES cares)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Cares DEFAULT_MSG CARES_LIBRARIES CARES_INCLUDE_DIR)
mark_as_advanced(
CARES_LIBRARIES
CARES_INCLUDE_DIR
)
if(NOT TARGET cares)
if("${CARES_LIBRARIES}" MATCHES ".*.a$")
add_library(cares STATIC IMPORTED)
else()
add_library(cares SHARED IMPORTED)
endif()
set_target_properties(
cares
PROPERTIES
IMPORTED_LOCATION ${CARES_LIBRARIES}
INTERFACE_INCLUDE_DIRECTORIES ${CARES_INCLUDE_DIR}
)
endif()
================================================
FILE: build/fbcode_builder/CMake/FindDoubleConversion.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
# Finds libdouble-conversion.
#
# This module defines:
# DOUBLE_CONVERSION_INCLUDE_DIR
# DOUBLE_CONVERSION_LIBRARY
#
find_path(DOUBLE_CONVERSION_INCLUDE_DIR double-conversion/double-conversion.h)
find_library(DOUBLE_CONVERSION_LIBRARY NAMES double-conversion)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
DoubleConversion
DEFAULT_MSG
DOUBLE_CONVERSION_LIBRARY DOUBLE_CONVERSION_INCLUDE_DIR)
mark_as_advanced(DOUBLE_CONVERSION_INCLUDE_DIR DOUBLE_CONVERSION_LIBRARY)
================================================
FILE: build/fbcode_builder/CMake/FindGMock.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# Find libgmock
#
# LIBGMOCK_DEFINES - List of defines when using libgmock.
# LIBGMOCK_INCLUDE_DIR - where to find gmock/gmock.h, etc.
# LIBGMOCK_LIBRARIES - List of libraries when using libgmock.
# LIBGMOCK_FOUND - True if libgmock found.
IF (LIBGMOCK_INCLUDE_DIR)
# Already in cache, be silent
SET(LIBGMOCK_FIND_QUIETLY TRUE)
ENDIF ()
find_package(GTest CONFIG QUIET)
if (TARGET GTest::gmock)
get_target_property(LIBGMOCK_DEFINES GTest::gtest INTERFACE_COMPILE_DEFINITIONS)
if (NOT ${LIBGMOCK_DEFINES})
# Explicitly set to empty string if not found to avoid it being
# set to NOTFOUND and breaking compilation
set(LIBGMOCK_DEFINES "")
endif()
get_target_property(LIBGMOCK_INCLUDE_DIR GTest::gtest INTERFACE_INCLUDE_DIRECTORIES)
set(LIBGMOCK_LIBRARIES GTest::gmock_main GTest::gmock GTest::gtest)
set(LIBGMOCK_FOUND ON)
message(STATUS "Found gmock via config, defines=${LIBGMOCK_DEFINES}, include=${LIBGMOCK_INCLUDE_DIR}, libs=${LIBGMOCK_LIBRARIES}")
else()
FIND_PATH(LIBGMOCK_INCLUDE_DIR gmock/gmock.h)
FIND_LIBRARY(LIBGMOCK_MAIN_LIBRARY_DEBUG NAMES gmock_maind)
FIND_LIBRARY(LIBGMOCK_MAIN_LIBRARY_RELEASE NAMES gmock_main)
FIND_LIBRARY(LIBGMOCK_LIBRARY_DEBUG NAMES gmockd)
FIND_LIBRARY(LIBGMOCK_LIBRARY_RELEASE NAMES gmock)
FIND_LIBRARY(LIBGTEST_LIBRARY_DEBUG NAMES gtestd)
FIND_LIBRARY(LIBGTEST_LIBRARY_RELEASE NAMES gtest)
find_package(Threads REQUIRED)
INCLUDE(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(LIBGMOCK_MAIN)
SELECT_LIBRARY_CONFIGURATIONS(LIBGMOCK)
SELECT_LIBRARY_CONFIGURATIONS(LIBGTEST)
set(LIBGMOCK_LIBRARIES
${LIBGMOCK_MAIN_LIBRARY}
${LIBGMOCK_LIBRARY}
${LIBGTEST_LIBRARY}
Threads::Threads
)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# The GTEST_LINKED_AS_SHARED_LIBRARY macro must be set properly on Windows.
#
# There isn't currently an easy way to determine if a library was compiled as
# a shared library on Windows, so just assume we've been built against a
# shared build of gmock for now.
SET(LIBGMOCK_DEFINES "GTEST_LINKED_AS_SHARED_LIBRARY=1" CACHE STRING "")
endif()
# handle the QUIETLY and REQUIRED arguments and set LIBGMOCK_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
GMock
DEFAULT_MSG
LIBGMOCK_MAIN_LIBRARY
LIBGMOCK_LIBRARY
LIBGTEST_LIBRARY
LIBGMOCK_LIBRARIES
LIBGMOCK_INCLUDE_DIR
)
MARK_AS_ADVANCED(
LIBGMOCK_DEFINES
LIBGMOCK_MAIN_LIBRARY
LIBGMOCK_LIBRARY
LIBGTEST_LIBRARY
LIBGMOCK_LIBRARIES
LIBGMOCK_INCLUDE_DIR
)
endif()
================================================
FILE: build/fbcode_builder/CMake/FindGflags.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# Find libgflags.
# There's a lot of compatibility cruft going on in here, both
# to deal with changes across the FB consumers of this and also
# to deal with variances in behavior of cmake itself.
#
# Since this file is named FindGflags.cmake the cmake convention
# is for the module to export both GFLAGS_FOUND and Gflags_FOUND.
# The convention expected by consumers is that we export the
# following variables, even though these do not match the cmake
# conventions:
#
# LIBGFLAGS_INCLUDE_DIR - where to find gflags/gflags.h, etc.
# LIBGFLAGS_LIBRARY - List of libraries when using libgflags.
# LIBGFLAGS_FOUND - True if libgflags found.
#
# We need to be able to locate gflags both from an installed
# cmake config file and just from the raw headers and libs, so
# test for the former and then the latter, and then stick
# the results together and export them into the variables
# listed above.
#
# For forwards compatibility, we export the following variables:
#
# gflags_INCLUDE_DIR - where to find gflags/gflags.h, etc.
# gflags_TARGET / GFLAGS_TARGET / gflags_LIBRARIES
# - List of libraries when using libgflags.
# gflags_FOUND - True if libgflags found.
#
IF (LIBGFLAGS_INCLUDE_DIR)
# Already in cache, be silent
SET(Gflags_FIND_QUIETLY TRUE)
ENDIF ()
find_package(gflags CONFIG QUIET)
if (gflags_FOUND)
if (NOT Gflags_FIND_QUIETLY)
message(STATUS "Found gflags from package config ${gflags_CONFIG}")
endif()
# Re-export the config-specified libs with our local names
set(LIBGFLAGS_LIBRARY ${gflags_LIBRARIES})
set(LIBGFLAGS_INCLUDE_DIR ${gflags_INCLUDE_DIR})
if(NOT EXISTS "${gflags_INCLUDE_DIR}")
# The gflags-devel RPM on recent RedHat-based systems is somewhat broken.
# RedHat symlinks /lib64 to /usr/lib64, and this breaks some of the
# relative path computation performed in gflags-config.cmake. The package
# config file ends up being found via /lib64, but the relative path
# computation it does only works if it was found in /usr/lib64.
# If gflags_INCLUDE_DIR does not actually exist, simply default it to
# /usr/include on these systems.
set(LIBGFLAGS_INCLUDE_DIR "/usr/include")
set(GFLAGS_INCLUDE_DIR "/usr/include")
endif()
set(LIBGFLAGS_FOUND ${gflags_FOUND})
# cmake module compat
set(GFLAGS_FOUND ${gflags_FOUND})
set(Gflags_FOUND ${gflags_FOUND})
else()
FIND_PATH(LIBGFLAGS_INCLUDE_DIR gflags/gflags.h)
FIND_LIBRARY(LIBGFLAGS_LIBRARY_DEBUG NAMES gflagsd gflags_staticd)
FIND_LIBRARY(LIBGFLAGS_LIBRARY_RELEASE NAMES gflags gflags_static)
INCLUDE(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(LIBGFLAGS)
# handle the QUIETLY and REQUIRED arguments and set LIBGFLAGS_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(gflags DEFAULT_MSG LIBGFLAGS_LIBRARY LIBGFLAGS_INCLUDE_DIR)
# cmake module compat
set(Gflags_FOUND ${GFLAGS_FOUND})
# compat with some existing FindGflags consumers
set(LIBGFLAGS_FOUND ${GFLAGS_FOUND})
# Compat with the gflags CONFIG based detection
set(gflags_FOUND ${GFLAGS_FOUND})
set(gflags_INCLUDE_DIR ${LIBGFLAGS_INCLUDE_DIR})
set(gflags_LIBRARIES ${LIBGFLAGS_LIBRARY})
set(GFLAGS_TARGET ${LIBGFLAGS_LIBRARY})
set(gflags_TARGET ${LIBGFLAGS_LIBRARY})
MARK_AS_ADVANCED(LIBGFLAGS_LIBRARY LIBGFLAGS_INCLUDE_DIR)
endif()
# Compat with the gflags CONFIG based detection
if (LIBGFLAGS_FOUND AND NOT TARGET gflags)
add_library(gflags UNKNOWN IMPORTED)
if(TARGET gflags-shared)
# If the installed gflags CMake package config defines a gflags-shared
# target but not gflags, just make the gflags target that we define
# depend on the gflags-shared target.
target_link_libraries(gflags INTERFACE gflags-shared)
# Export LIBGFLAGS_LIBRARY as the gflags-shared target in this case.
set(LIBGFLAGS_LIBRARY gflags-shared)
else()
set_target_properties(
gflags
PROPERTIES
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
IMPORTED_LOCATION "${LIBGFLAGS_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${LIBGFLAGS_INCLUDE_DIR}"
)
endif()
endif()
================================================
FILE: build/fbcode_builder/CMake/FindGlog.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# - Try to find Glog
# Once done, this will define
#
# GLOG_FOUND - system has Glog
# GLOG_INCLUDE_DIRS - the Glog include directories
# GLOG_LIBRARIES - link these to use Glog
include(FindPackageHandleStandardArgs)
include(SelectLibraryConfigurations)
find_library(GLOG_LIBRARY_RELEASE glog
PATHS ${GLOG_LIBRARYDIR})
find_library(GLOG_LIBRARY_DEBUG glogd
PATHS ${GLOG_LIBRARYDIR})
find_path(GLOG_INCLUDE_DIR glog/logging.h
PATHS ${GLOG_INCLUDEDIR})
select_library_configurations(GLOG)
find_package_handle_standard_args(Glog DEFAULT_MSG
GLOG_LIBRARY
GLOG_INCLUDE_DIR)
mark_as_advanced(
GLOG_LIBRARY
GLOG_INCLUDE_DIR)
set(GLOG_LIBRARIES ${GLOG_LIBRARY})
set(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIR})
if (NOT TARGET glog::glog)
add_library(glog::glog UNKNOWN IMPORTED)
set_target_properties(glog::glog PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${GLOG_INCLUDE_DIRS}")
set_target_properties(glog::glog PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" IMPORTED_LOCATION "${GLOG_LIBRARIES}")
set_target_properties(glog::glog PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "GLOG_USE_GLOG_EXPORT")
find_package(Gflags)
if(GFLAGS_FOUND)
message(STATUS "Found gflags as a dependency of glog::glog, include=${LIBGFLAGS_INCLUDE_DIR}, libs=${LIBGFLAGS_LIBRARY}")
set_property(TARGET glog::glog APPEND PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES ${LIBGFLAGS_LIBRARY})
endif()
find_package(LibUnwind)
if(LIBUNWIND_FOUND)
message(STATUS "Found LibUnwind as a dependency of glog::glog, include=${LIBUNWIND_INCLUDE_DIR}, libs=${LIBUNWIND_LIBRARY}")
set_property(TARGET glog::glog APPEND PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES ${LIBUNWIND_LIBRARY})
endif()
endif()
================================================
FILE: build/fbcode_builder/CMake/FindLMDB.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
find_library(LMDB_LIBRARIES NAMES lmdb liblmdb)
mark_as_advanced(LMDB_LIBRARIES)
find_path(LMDB_INCLUDE_DIR NAMES lmdb.h)
mark_as_advanced(LMDB_INCLUDE_DIR)
find_package_handle_standard_args(
LMDB
REQUIRED_VARS LMDB_LIBRARIES LMDB_INCLUDE_DIR)
if(LMDB_FOUND)
set(LMDB_LIBRARIES ${LMDB_LIBRARIES})
set(LMDB_INCLUDE_DIR, ${LMDB_INCLUDE_DIR})
endif()
================================================
FILE: build/fbcode_builder/CMake/FindLibEvent.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
# - Find LibEvent (a cross event library)
# This module defines
# LIBEVENT_INCLUDE_DIR, where to find LibEvent headers
# LIBEVENT_LIB, LibEvent libraries
# LibEvent_FOUND, If false, do not try to use libevent
set(LibEvent_EXTRA_PREFIXES /usr/local /opt/local "$ENV{HOME}")
foreach(prefix ${LibEvent_EXTRA_PREFIXES})
list(APPEND LibEvent_INCLUDE_PATHS "${prefix}/include")
list(APPEND LibEvent_LIB_PATHS "${prefix}/lib")
endforeach()
find_package(Libevent CONFIG QUIET)
if (TARGET event)
# Re-export the config under our own names
# Somewhat gross, but some vcpkg installed libevents have a relative
# `include` path exported into LIBEVENT_INCLUDE_DIRS, which triggers
# a cmake error because it resolves to the `include` dir within the
# folly repo, which is not something cmake allows to be in the
# INTERFACE_INCLUDE_DIRECTORIES. Thankfully on such a system the
# actual include directory is already part of the global include
# directories, so we can just skip it.
if (NOT "${LIBEVENT_INCLUDE_DIRS}" STREQUAL "include")
set(LIBEVENT_INCLUDE_DIR ${LIBEVENT_INCLUDE_DIRS})
else()
set(LIBEVENT_INCLUDE_DIR)
endif()
# Unfortunately, with a bare target name `event`, downstream consumers
# of the package that depends on `Libevent` located via CONFIG end
# up exporting just a bare `event` in their libraries. This is problematic
# because this in interpreted as just `-levent` with no library path.
# When libevent is not installed in the default installation prefix
# this results in linker errors.
# To resolve this, we ask cmake to lookup the full path to the library
# and use that instead.
cmake_policy(PUSH)
if(POLICY CMP0026)
# Allow reading the LOCATION property
cmake_policy(SET CMP0026 OLD)
endif()
get_target_property(LIBEVENT_LIB event LOCATION)
cmake_policy(POP)
set(LibEvent_FOUND ${Libevent_FOUND})
if (NOT LibEvent_FIND_QUIETLY)
message(STATUS "Found libevent from package config include=${LIBEVENT_INCLUDE_DIRS} lib=${LIBEVENT_LIB}")
endif()
else()
find_path(LIBEVENT_INCLUDE_DIR event.h PATHS ${LibEvent_INCLUDE_PATHS})
find_library(LIBEVENT_LIB NAMES event PATHS ${LibEvent_LIB_PATHS})
if (LIBEVENT_LIB AND LIBEVENT_INCLUDE_DIR)
set(LibEvent_FOUND TRUE)
set(LIBEVENT_LIB ${LIBEVENT_LIB})
else ()
set(LibEvent_FOUND FALSE)
endif ()
if (LibEvent_FOUND)
if (NOT LibEvent_FIND_QUIETLY)
message(STATUS "Found libevent: ${LIBEVENT_LIB}")
endif ()
else ()
if (LibEvent_FIND_REQUIRED)
message(FATAL_ERROR "Could NOT find libevent.")
endif ()
message(STATUS "libevent NOT found.")
endif ()
mark_as_advanced(
LIBEVENT_LIB
LIBEVENT_INCLUDE_DIR
)
endif()
================================================
FILE: build/fbcode_builder/CMake/FindLibUnwind.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include(FindPackageHandleStandardArgs)
# Prefer pkg-config: picks up transitive deps (e.g. lzma, zlib) that
# static libunwind needs but a bare find_library would miss.
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_LIBUNWIND QUIET libunwind)
endif()
if(PC_LIBUNWIND_FOUND)
find_path(LIBUNWIND_INCLUDE_DIR NAMES libunwind.h
HINTS ${PC_LIBUNWIND_INCLUDE_DIRS}
PATH_SUFFIXES libunwind)
mark_as_advanced(LIBUNWIND_INCLUDE_DIR)
# Resolve each library from the static set (Libs + Libs.private) to a
# full path. This gives the linker everything it needs for a fully-static
# link without leaking imported targets through cmake exports.
set(LIBUNWIND_LIBRARIES "")
foreach(_lib IN LISTS PC_LIBUNWIND_STATIC_LIBRARIES)
find_library(_libunwind_dep_${_lib} NAMES ${_lib}
HINTS ${PC_LIBUNWIND_STATIC_LIBRARY_DIRS})
if(_libunwind_dep_${_lib})
list(APPEND LIBUNWIND_LIBRARIES ${_libunwind_dep_${_lib}})
else()
# Fall back to bare name; the linker will resolve -l.
list(APPEND LIBUNWIND_LIBRARIES ${_lib})
endif()
unset(_libunwind_dep_${_lib} CACHE)
endforeach()
set(LIBUNWIND_LIBRARY "${LIBUNWIND_LIBRARIES}")
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUnwind
REQUIRED_VARS LIBUNWIND_LIBRARIES LIBUNWIND_INCLUDE_DIR)
else()
# Fallback for systems without pkg-config.
# When using prepackaged LLVM libunwind on Ubuntu, its includes are
# installed in a subdirectory.
find_path(LIBUNWIND_INCLUDE_DIR NAMES libunwind.h PATH_SUFFIXES libunwind)
mark_as_advanced(LIBUNWIND_INCLUDE_DIR)
find_library(LIBUNWIND_LIBRARY NAMES unwind)
mark_as_advanced(LIBUNWIND_LIBRARY)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUnwind
REQUIRED_VARS LIBUNWIND_LIBRARY LIBUNWIND_INCLUDE_DIR)
endif()
if(LibUnwind_FOUND)
if(NOT LIBUNWIND_LIBRARIES)
set(LIBUNWIND_LIBRARIES ${LIBUNWIND_LIBRARY})
endif()
set(LIBUNWIND_INCLUDE_DIRS ${LIBUNWIND_INCLUDE_DIR})
endif()
================================================
FILE: build/fbcode_builder/CMake/FindLibiberty.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
find_path(LIBIBERTY_INCLUDE_DIR NAMES libiberty.h PATH_SUFFIXES libiberty)
mark_as_advanced(LIBIBERTY_INCLUDE_DIR)
find_library(LIBIBERTY_LIBRARY NAMES iberty)
mark_as_advanced(LIBIBERTY_LIBRARY)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
LIBIBERTY
REQUIRED_VARS LIBIBERTY_LIBRARY LIBIBERTY_INCLUDE_DIR)
if(LIBIBERTY_FOUND)
set(LIBIBERTY_LIBRARIES ${LIBIBERTY_LIBRARY})
set(LIBIBERTY_INCLUDE_DIRS ${LIBIBERTY_INCLUDE_DIR})
endif()
================================================
FILE: build/fbcode_builder/CMake/FindPCRE.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
include(FindPackageHandleStandardArgs)
find_path(PCRE_INCLUDE_DIR NAMES pcre.h)
find_library(PCRE_LIBRARY NAMES pcre)
find_package_handle_standard_args(
PCRE
DEFAULT_MSG
PCRE_LIBRARY
PCRE_INCLUDE_DIR
)
mark_as_advanced(PCRE_INCLUDE_DIR PCRE_LIBRARY)
================================================
FILE: build/fbcode_builder/CMake/FindPCRE2.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
include(FindPackageHandleStandardArgs)
find_path(PCRE2_INCLUDE_DIR NAMES pcre2.h)
find_library(PCRE2_LIBRARY NAMES pcre2-8)
find_package_handle_standard_args(
PCRE2
DEFAULT_MSG
PCRE2_LIBRARY
PCRE2_INCLUDE_DIR
)
set(PCRE2_DEFINES "PCRE2_CODE_UNIT_WIDTH=8")
mark_as_advanced(PCRE2_INCLUDE_DIR PCRE2_LIBRARY PCRE2_DEFINES)
================================================
FILE: build/fbcode_builder/CMake/FindRe2.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2.
find_library(RE2_LIBRARY re2)
mark_as_advanced(RE2_LIBRARY)
find_path(RE2_INCLUDE_DIR NAMES re2/re2.h)
mark_as_advanced(RE2_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
RE2
REQUIRED_VARS RE2_LIBRARY RE2_INCLUDE_DIR)
if(RE2_FOUND)
set(RE2_LIBRARY ${RE2_LIBRARY})
set(RE2_INCLUDE_DIR, ${RE2_INCLUDE_DIR})
endif()
================================================
FILE: build/fbcode_builder/CMake/FindSodium.cmake
================================================
# Written in 2016 by Henrik Steffen Gaßmann
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
########################################################################
# Tries to find the local libsodium installation.
#
# On Windows the sodium_DIR environment variable is used as a default
# hint which can be overridden by setting the corresponding cmake variable.
#
# Once done the following variables will be defined:
#
# sodium_FOUND
# sodium_INCLUDE_DIR
# sodium_LIBRARY_DEBUG
# sodium_LIBRARY_RELEASE
#
#
# Furthermore an imported "sodium" target is created.
#
if (CMAKE_C_COMPILER_ID STREQUAL "GNU"
OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(_GCC_COMPATIBLE 1)
endif()
# static library option
if (NOT DEFINED sodium_USE_STATIC_LIBS)
option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF)
endif()
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
unset(sodium_LIBRARY CACHE)
unset(sodium_LIBRARY_DEBUG CACHE)
unset(sodium_LIBRARY_RELEASE CACHE)
unset(sodium_DLL_DEBUG CACHE)
unset(sodium_DLL_RELEASE CACHE)
set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable")
endif()
########################################################################
# UNIX
if (UNIX)
# import pkg-config
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(sodium_PKG QUIET libsodium)
endif()
if(sodium_USE_STATIC_LIBS)
foreach(_libname ${sodium_PKG_STATIC_LIBRARIES})
if (NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending with .a
list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a")
endif()
endforeach()
list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES)
# if pkgconfig for libsodium doesn't provide
# static lib info, then override PKG_STATIC here..
if (NOT sodium_PKG_STATIC_FOUND)
set(sodium_PKG_STATIC_LIBRARIES libsodium.a)
endif()
set(XPREFIX sodium_PKG_STATIC)
else()
if (NOT sodium_PKG_FOUND)
set(sodium_PKG_LIBRARIES sodium)
endif()
set(XPREFIX sodium_PKG)
endif()
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${${XPREFIX}_INCLUDE_DIRS}
)
find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES}
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
########################################################################
# Windows
elseif (WIN32)
set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
mark_as_advanced(sodium_DIR)
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${sodium_DIR}
PATH_SUFFIXES include
)
if (MSVC)
# detect target architecture
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[
#if defined _M_IX86
#error ARCH_VALUE x86_32
#elif defined _M_X64
#error ARCH_VALUE x86_64
#endif
#error ARCH_VALUE unknown
]=])
try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp"
OUTPUT_VARIABLE _COMPILATION_LOG
)
string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}")
# construct library path
if (_TARGET_ARCH STREQUAL "x86_32")
string(APPEND _PLATFORM_PATH "Win32")
elseif(_TARGET_ARCH STREQUAL "x86_64")
string(APPEND _PLATFORM_PATH "x64")
else()
message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.")
endif()
string(APPEND _PLATFORM_PATH "/$$CONFIG$$")
if (MSVC_VERSION LESS 1900)
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
else()
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
endif()
string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")
if (sodium_USE_STATIC_LIBS)
string(APPEND _PLATFORM_PATH "/static")
else()
string(APPEND _PLATFORM_PATH "/dynamic")
endif()
string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}")
string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}")
find_library(sodium_LIBRARY_DEBUG libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_LIBRARY_RELEASE libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
if (NOT sodium_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
find_library(sodium_DLL_DEBUG libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_DLL_RELEASE libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})
endif()
elseif(_GCC_COMPATIBLE)
if (sodium_USE_STATIC_LIBS)
find_library(sodium_LIBRARY_DEBUG libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
else()
find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
file(GLOB _DLL
LIST_DIRECTORIES false
RELATIVE "${sodium_DIR}/bin"
"${sodium_DIR}/bin/libsodium*.dll"
)
find_library(sodium_DLL_DEBUG ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
find_library(sodium_DLL_RELEASE ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
endif()
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# unsupported
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# common stuff
# extract sodium version
if (sodium_INCLUDE_DIR)
set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h")
if (EXISTS _VERSION_HEADER)
file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1"
sodium_VERSION "${_VERSION_HEADER_CONTENT}")
set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE)
endif()
endif()
# communicate results
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
Sodium # The name must be either uppercase or match the filename case.
REQUIRED_VARS
sodium_LIBRARY_RELEASE
sodium_LIBRARY_DEBUG
sodium_INCLUDE_DIR
VERSION_VAR
sodium_VERSION
)
if(Sodium_FOUND)
set(sodium_LIBRARIES
optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG})
endif()
# mark file paths as advanced
mark_as_advanced(sodium_INCLUDE_DIR)
mark_as_advanced(sodium_LIBRARY_DEBUG)
mark_as_advanced(sodium_LIBRARY_RELEASE)
if (WIN32)
mark_as_advanced(sodium_DLL_DEBUG)
mark_as_advanced(sodium_DLL_RELEASE)
endif()
# create imported target
if(sodium_USE_STATIC_LIBS)
set(_LIB_TYPE STATIC)
else()
set(_LIB_TYPE SHARED)
endif()
if(NOT TARGET sodium)
add_library(sodium ${_LIB_TYPE} IMPORTED)
endif()
set_target_properties(sodium PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
)
if (sodium_USE_STATIC_LIBS)
set_target_properties(sodium PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC"
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
else()
if (UNIX)
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
elseif (WIN32)
set_target_properties(sodium PROPERTIES
IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}"
IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}"
)
if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}"
)
endif()
if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}"
)
endif()
endif()
endif()
================================================
FILE: build/fbcode_builder/CMake/FindXxhash.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# - Try to find Facebook xxhash library
# This will define
# Xxhash_FOUND
# Xxhash_INCLUDE_DIR
# Xxhash_LIBRARY
#
find_path(Xxhash_INCLUDE_DIR NAMES xxhash.h)
find_library(Xxhash_LIBRARY_RELEASE NAMES xxhash)
include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(Xxhash)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
Xxhash DEFAULT_MSG
Xxhash_LIBRARY Xxhash_INCLUDE_DIR
)
if (Xxhash_FOUND)
message(STATUS "Found xxhash: ${Xxhash_LIBRARY}")
endif()
mark_as_advanced(Xxhash_INCLUDE_DIR Xxhash_LIBRARY)
================================================
FILE: build/fbcode_builder/CMake/FindZstd.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# - Try to find Facebook zstd library
# This will define
# ZSTD_FOUND
# ZSTD_INCLUDE_DIR
# ZSTD_LIBRARY
#
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)
include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(ZSTD)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
Zstd DEFAULT_MSG
ZSTD_LIBRARY ZSTD_INCLUDE_DIR
)
if (ZSTD_FOUND)
message(STATUS "Found Zstd: ${ZSTD_LIBRARY}")
endif()
mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)
================================================
FILE: build/fbcode_builder/CMake/Findibverbs.cmake
================================================
# Copyright (c) Facebook, Inc. and its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Find the ibverbs libraries
#
# The following variables are optionally searched for defaults
# IBVERBS_ROOT_DIR: Base directory where all ibverbs components are found
# IBVERBS_INCLUDE_DIR: Directory where ibverbs headers are found
# IBVERBS_LIB_DIR: Directory where ibverbs libraries are found
# The following are set after configuration is done:
# IBVERBS_FOUND
# IBVERBS_INCLUDE_DIRS
# IBVERBS_LIBRARIES
# IBVERBS_VERSION
find_path(IBVERBS_INCLUDE_DIRS
NAMES infiniband/verbs.h
HINTS
${IBVERBS_INCLUDE_DIR}
${IBVERBS_ROOT_DIR}
${IBVERBS_ROOT_DIR}/include)
find_library(IBVERBS_LIBRARIES
NAMES ibverbs
HINTS
${IBVERBS_LIB_DIR}
${IBVERBS_ROOT_DIR}
${IBVERBS_ROOT_DIR}/lib)
# Try to determine the rdma-core version
if(IBVERBS_INCLUDE_DIRS AND IBVERBS_LIBRARIES)
# First try using pkg-config if available
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_check_modules(PC_RDMA_CORE QUIET rdma-core)
if(PC_RDMA_CORE_VERSION)
set(IBVERBS_VERSION ${PC_RDMA_CORE_VERSION})
endif()
endif()
# If pkg-config didn't work, try to extract version from library filename
# According to rdma-core Documentation/versioning.md:
# Library filename format:
# libibverbs.so.SONAME.ABI.PACKAGE_VERSION_MAIN[.PACKAGE_VERSION_BRANCH]
# Where:
# - SONAME: Major version (1st field)
# - ABI: ABI version number (2nd field)
# - PACKAGE_VERSION_MAIN: Main package version (3rd field)
# - PACKAGE_VERSION_BRANCH: Optional counter for branched stable
# releases (4th field, part of PACKAGE_VERSION)
# Example: libibverbs.so.1.14.57.0 → SONAME=1, ABI=14,
# PACKAGE_VERSION=57.0
if(NOT IBVERBS_VERSION)
# Get the real path of the library (follows symlinks)
get_filename_component(IBVERBS_REAL_PATH "${IBVERBS_LIBRARIES}" REALPATH)
get_filename_component(IBVERBS_LIB_NAME "${IBVERBS_REAL_PATH}" NAME)
# Extract version from filename
if(IBVERBS_LIB_NAME MATCHES
"libibverbs\\.so\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)")
# Four-component version: PACKAGE_VERSION_MAIN.PACKAGE_VERSION_BRANCH
set(IBVERBS_VERSION_MAJOR ${CMAKE_MATCH_3})
set(IBVERBS_VERSION_MINOR ${CMAKE_MATCH_4})
set(IBVERBS_VERSION "${IBVERBS_VERSION_MAJOR}.${IBVERBS_VERSION_MINOR}")
elseif(IBVERBS_LIB_NAME MATCHES
"libibverbs\\.so\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)")
# Three-component version: PACKAGE_VERSION_MAIN only
set(IBVERBS_VERSION_MAJOR ${CMAKE_MATCH_3})
set(IBVERBS_VERSION "${IBVERBS_VERSION_MAJOR}.0")
else()
# If we can't parse the filename, set to empty string
# Feature detection will be done in CMakeLists.txt
set(IBVERBS_VERSION "")
endif()
endif()
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(ibverbs
REQUIRED_VARS IBVERBS_INCLUDE_DIRS IBVERBS_LIBRARIES
VERSION_VAR IBVERBS_VERSION)
mark_as_advanced(IBVERBS_INCLUDE_DIRS IBVERBS_LIBRARIES IBVERBS_VERSION)
================================================
FILE: build/fbcode_builder/CMake/RustStaticLibrary.cmake
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
include(FBCMakeParseArgs)
set(
USE_CARGO_VENDOR AUTO CACHE STRING
"Download Rust Crates from an internally vendored location"
)
set_property(CACHE USE_CARGO_VENDOR PROPERTY STRINGS AUTO ON OFF)
set(
GENERATE_CARGO_VENDOR_CONFIG AUTO CACHE STRING
"Whether to generate Rust cargo vendor config or use existing"
)
set_property(CACHE GENERATE_CARGO_VENDOR_CONFIG PROPERTY STRINGS AUTO ON OFF)
set(RUST_VENDORED_CRATES_DIR "$ENV{RUST_VENDORED_CRATES_DIR}")
if("${USE_CARGO_VENDOR}" STREQUAL "AUTO")
if(EXISTS "${RUST_VENDORED_CRATES_DIR}")
set(USE_CARGO_VENDOR ON)
else()
set(USE_CARGO_VENDOR OFF)
endif()
endif()
if("${GENERATE_CARGO_VENDOR_CONFIG}" STREQUAL "AUTO")
set(GENERATE_CARGO_VENDOR_CONFIG "${USE_CARGO_VENDOR}")
endif()
if(GENERATE_CARGO_VENDOR_CONFIG)
if(NOT EXISTS "${RUST_VENDORED_CRATES_DIR}")
message(
FATAL "vendored rust crates not present: "
"${RUST_VENDORED_CRATES_DIR}"
)
endif()
set(RUST_CARGO_HOME "${CMAKE_BINARY_DIR}/_cargo_home")
file(MAKE_DIRECTORY "${RUST_CARGO_HOME}")
file(
TO_NATIVE_PATH "${RUST_VENDORED_CRATES_DIR}"
ESCAPED_RUST_VENDORED_CRATES_DIR
)
string(
REPLACE "\\" "\\\\"
ESCAPED_RUST_VENDORED_CRATES_DIR
"${ESCAPED_RUST_VENDORED_CRATES_DIR}"
)
file(
WRITE "${RUST_CARGO_HOME}/config"
"[source.crates-io]\n"
"replace-with = \"vendored-sources\"\n"
"\n"
"[source.vendored-sources]\n"
"directory = \"${ESCAPED_RUST_VENDORED_CRATES_DIR}\"\n"
)
endif()
find_program(CARGO_COMMAND cargo REQUIRED)
# Cargo is a build system in itself, and thus will try to take advantage of all
# the cores on the system. Unfortunately, this conflicts with Ninja, since it
# also tries to utilize all the cores. This can lead to a system that is
# completely overloaded with compile jobs to the point where nothing else can
# be achieved on the system.
#
# Let's inform Ninja of this fact so it won't try to spawn other jobs while
# Rust being compiled.
set_property(GLOBAL APPEND PROPERTY JOB_POOLS rust_job_pool=1)
# This function creates an interface library target based on the static library
# built by Cargo. It will call Cargo to build a staticlib and generate a CMake
# interface library with it.
#
# This function requires `find_package(Python COMPONENTS Interpreter)`.
#
# You need to set `lib:crate-type = ["staticlib"]` in your Cargo.toml to make
# Cargo build static library.
#
# ```cmake
# rust_static_library( [CRATE ] [FEATURES ] [USE_CXX_INCLUDE])
# ```
#
# Parameters:
# - TARGET:
# Name of the target name. This function will create an interface library
# target with this name.
# - CRATE_NAME:
# Name of the crate. This parameter is optional. If unspecified, it will
# fallback to `${TARGET}`.
# - FEATURE_NAME:
# Name of the Rust feature to enable.
# - USE_CXX_INCLUDE:
# Include cxx.rs include path in `${TARGET}` INTERFACE.
#
# This function creates two targets:
# - "${TARGET}": an interface library target contains the static library built
# from Cargo.
# - "${TARGET}.cargo": an internal custom target that invokes Cargo.
#
# If you are going to use this static library from C/C++, you will need to
# write header files for the library (or generate with cbindgen) and bind these
# headers with the interface library.
#
function(rust_static_library TARGET)
fb_cmake_parse_args(ARG "USE_CXX_INCLUDE" "CRATE;FEATURES" "" "${ARGN}")
if(DEFINED ARG_CRATE)
set(crate_name "${ARG_CRATE}")
else()
set(crate_name "${TARGET}")
endif()
if(DEFINED ARG_FEATURES)
set(features --features ${ARG_FEATURES})
else()
set(features )
endif()
set(cargo_target "${TARGET}.cargo")
set(target_dir $,debug,release>)
set(staticlib_name "${CMAKE_STATIC_LIBRARY_PREFIX}${crate_name}${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(rust_staticlib "${CMAKE_CURRENT_BINARY_DIR}/${target_dir}/${staticlib_name}")
if(DEFINED ARG_FEATURES)
set(cargo_flags build $,,--release> -p ${crate_name} --features ${ARG_FEATURES} --config fbcode_build=false)
else()
set(cargo_flags build $,,--release> -p ${crate_name} --config fbcode_build=false)
endif()
if(USE_CARGO_VENDOR)
set(extra_cargo_env "CARGO_HOME=${RUST_CARGO_HOME}")
set(cargo_flags ${cargo_flags})
endif()
add_custom_target(
${cargo_target}
COMMAND
"${CMAKE_COMMAND}" -E remove -f "${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock"
COMMAND
"${CMAKE_COMMAND}" -E env
"CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}"
${extra_cargo_env}
${CARGO_COMMAND}
${cargo_flags}
COMMENT "Building Rust crate '${crate_name}'..."
JOB_POOL rust_job_pool
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
BYPRODUCTS
"${CMAKE_CURRENT_BINARY_DIR}/debug/${staticlib_name}"
"${CMAKE_CURRENT_BINARY_DIR}/release/${staticlib_name}"
)
add_library(${TARGET} INTERFACE)
add_dependencies(${TARGET} ${cargo_target})
set_target_properties(
${TARGET}
PROPERTIES
INTERFACE_STATICLIB_OUTPUT_PATH "${rust_staticlib}"
INTERFACE_INSTALL_LIBNAME
"${CMAKE_STATIC_LIBRARY_PREFIX}${crate_name}_rs${CMAKE_STATIC_LIBRARY_SUFFIX}"
)
if(DEFINED ARG_USE_CXX_INCLUDE)
target_include_directories(
${TARGET}
INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/cxxbridge/
)
endif()
target_link_libraries(
${TARGET}
INTERFACE "$"
)
endfunction()
# This function instructs CMake to define a target that will use `cargo build`
# to build a bin crate referenced by the Cargo.toml file in the current source
# directory.
# It accepts a single `TARGET` parameter which will be passed as the package
# name to `cargo build -p TARGET`. If binary has different name as package,
# use optional flag BINARY_NAME to override it.
# It also accepts a `FEATURES` parameter if you want to enable certain features
# in your Rust binary.
# The CMake target will be registered to build by default as part of the
# ALL target.
function(rust_executable TARGET)
fb_cmake_parse_args(ARG "" "BINARY_NAME;FEATURES" "" "${ARGN}")
set(crate_name "${TARGET}")
set(cargo_target "${TARGET}.cargo")
set(target_dir $,debug,release>)
if(DEFINED ARG_BINARY_NAME)
set(executable_name "${ARG_BINARY_NAME}${CMAKE_EXECUTABLE_SUFFIX}")
else()
set(executable_name "${crate_name}${CMAKE_EXECUTABLE_SUFFIX}")
endif()
if(DEFINED ARG_FEATURES)
set(features --features ${ARG_FEATURES})
else()
set(features )
endif()
if(DEFINED ARG_FEATURES)
set(cargo_flags build $,,--release> -p ${crate_name} --features ${ARG_FEATURES})
else()
set(cargo_flags build $,,--release> -p ${crate_name})
endif()
if(USE_CARGO_VENDOR)
set(extra_cargo_env "CARGO_HOME=${RUST_CARGO_HOME}")
set(cargo_flags ${cargo_flags})
endif()
add_custom_target(
${cargo_target}
ALL
COMMAND
"${CMAKE_COMMAND}" -E remove -f "${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock"
COMMAND
"${CMAKE_COMMAND}" -E env
"CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR}"
${extra_cargo_env}
${CARGO_COMMAND}
${cargo_flags}
COMMENT "Building Rust executable '${crate_name}'..."
JOB_POOL rust_job_pool
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
BYPRODUCTS
"${CMAKE_CURRENT_BINARY_DIR}/debug/${executable_name}"
"${CMAKE_CURRENT_BINARY_DIR}/release/${executable_name}"
)
set_property(TARGET "${cargo_target}"
PROPERTY EXECUTABLE "${CMAKE_CURRENT_BINARY_DIR}/${target_dir}/${executable_name}")
endfunction()
# This function can be used to install the executable generated by a prior
# call to the `rust_executable` function.
# It requires a `TARGET` parameter to identify the target to be installed,
# and an optional `DESTINATION` parameter to specify the installation
# directory. If DESTINATION is not specified then the `bin` directory
# will be assumed.
function(install_rust_executable TARGET)
# Parse the arguments
set(one_value_args DESTINATION)
set(multi_value_args)
fb_cmake_parse_args(
ARG "" "${one_value_args}" "${multi_value_args}" "${ARGN}"
)
if(NOT DEFINED ARG_DESTINATION)
set(ARG_DESTINATION bin)
endif()
get_target_property(foo "${TARGET}.cargo" EXECUTABLE)
install(
PROGRAMS "${foo}"
DESTINATION "${ARG_DESTINATION}"
)
endfunction()
# This function installs the interface target generated from the function
# `rust_static_library`. Use this function if you want to export your Rust
# target to external CMake targets.
#
# ```cmake
# install_rust_static_library(
#
# INSTALL_DIR
# [EXPORT ]
# )
# ```
#
# Parameters:
# - TARGET: Name of the Rust static library target.
# - EXPORT_NAME: Name of the exported target.
# - INSTALL_DIR: Path to the directory where this library will be installed.
#
function(install_rust_static_library TARGET)
fb_cmake_parse_args(ARG "" "EXPORT;INSTALL_DIR" "" "${ARGN}")
get_property(
staticlib_output_path
TARGET "${TARGET}"
PROPERTY INTERFACE_STATICLIB_OUTPUT_PATH
)
get_property(
staticlib_output_name
TARGET "${TARGET}"
PROPERTY INTERFACE_INSTALL_LIBNAME
)
if(NOT DEFINED staticlib_output_path)
message(FATAL_ERROR "Not a rust_static_library target.")
endif()
if(NOT DEFINED ARG_INSTALL_DIR)
message(FATAL_ERROR "Missing required argument.")
endif()
if(DEFINED ARG_EXPORT)
set(install_export_args EXPORT "${ARG_EXPORT}")
endif()
set(install_interface_dir "${ARG_INSTALL_DIR}")
if(NOT IS_ABSOLUTE "${install_interface_dir}")
set(install_interface_dir "\${_IMPORT_PREFIX}/${install_interface_dir}")
endif()
target_link_libraries(
${TARGET} INTERFACE
"$"
)
install(
TARGETS ${TARGET}
${install_export_args}
LIBRARY DESTINATION ${ARG_INSTALL_DIR}
)
install(
FILES ${staticlib_output_path}
RENAME ${staticlib_output_name}
DESTINATION ${ARG_INSTALL_DIR}
)
endfunction()
# This function creates C++ bindings using the [cxx] crate.
#
# Original function found here: https://github.com/corrosion-rs/corrosion/blob/master/cmake/Corrosion.cmake#L1390
# Simplified for use as part of RustStaticLibrary module. License below.
#
# MIT License
#
# Copyright (c) 2018 Andrew Gaspar
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# The rules approximately do the following:
# - Check which version of `cxx` the Rust crate depends on.
# - Check if the exact same version of `cxxbridge-cmd` is installed
# - If not, create a rule to build the exact same version of `cxxbridge-cmd`.
# - Create rules to run `cxxbridge` and generate
# - The `rust/cxx.h` header
# - A header and source file for the specified CXX_BRIDGE_FILE.
# - The generated sources (and header include directories) are added to the
# `${TARGET}` CMake library target.
#
# ```cmake
# rust_cxx_bridge( [CRATE ] [LIBS ])
# ```
#
# Parameters:
# - TARGET:
# Name of the target name. The target that the bridge will be included with.
# - CXX_BRIDGE_FILE:
# Name of the file that include the cxxbridge (e.g., "src/ffi.rs").
# - CRATE_NAME:
# Name of the crate. This parameter is optional. If unspecified, it will
# fallback to `${TARGET}`.
# - LIBS [ ...]:
# A list of libraries that this library depends on.
#
function(rust_cxx_bridge TARGET CXX_BRIDGE_FILE)
fb_cmake_parse_args(ARG "" "CRATE" "LIBS" "${ARGN}")
if(DEFINED ARG_CRATE)
set(crate_name "${ARG_CRATE}")
else()
set(crate_name "${TARGET}")
endif()
if(USE_CARGO_VENDOR)
set(extra_cargo_env "CARGO_HOME=${RUST_CARGO_HOME}")
endif()
execute_process(
COMMAND
"${CMAKE_COMMAND}" -E env
${extra_cargo_env}
"${CARGO_COMMAND}" tree -i cxx --depth=0
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE cxx_version_result
OUTPUT_VARIABLE cxx_version_output
)
if(NOT "${cxx_version_result}" EQUAL "0")
message(FATAL_ERROR "Crate ${crate_name} does not depend on cxx.")
endif()
if(cxx_version_output MATCHES "cxx v([0-9]+.[0-9]+.[0-9]+)")
set(cxx_required_version "${CMAKE_MATCH_1}")
else()
message(
FATAL_ERROR
"Failed to parse cxx version from cargo tree output: `cxx_version_output`")
endif()
# First check if a suitable version of cxxbridge is installed
find_program(INSTALLED_CXXBRIDGE cxxbridge PATHS "$ENV{HOME}/.cargo/bin/")
mark_as_advanced(INSTALLED_CXXBRIDGE)
if(INSTALLED_CXXBRIDGE)
execute_process(
COMMAND "${INSTALLED_CXXBRIDGE}" --version
OUTPUT_VARIABLE cxxbridge_version_output
)
if(cxxbridge_version_output MATCHES "cxxbridge ([0-9]+.[0-9]+.[0-9]+)")
set(cxxbridge_version "${CMAKE_MATCH_1}")
else()
set(cxxbridge_version "")
endif()
endif()
set(cxxbridge "")
if(cxxbridge_version)
if(cxxbridge_version VERSION_EQUAL cxx_required_version)
set(cxxbridge "${INSTALLED_CXXBRIDGE}")
if(NOT TARGET "cxxbridge_v${cxx_required_version}")
# Add an empty target.
add_custom_target("cxxbridge_v${cxx_required_version}")
endif()
endif()
endif()
# No suitable version of cxxbridge was installed,
# so use custom target to install correct version.
if(NOT cxxbridge)
if(NOT TARGET "cxxbridge_v${cxx_required_version}")
add_custom_command(
OUTPUT
"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge"
COMMAND
"${CMAKE_COMMAND}" -E make_directory
"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}"
COMMAND
"${CMAKE_COMMAND}" -E remove -f "${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock"
COMMAND
"${CMAKE_COMMAND}" -E env
${extra_cargo_env}
"${CARGO_COMMAND}" install cxxbridge-cmd
--version "${cxx_required_version}"
--root "${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}"
--quiet
COMMAND
"${CMAKE_COMMAND}" -E remove -f "${CMAKE_CURRENT_SOURCE_DIR}/Cargo.lock"
COMMENT "Installing cxxbridge (version ${cxx_required_version})"
)
add_custom_target(
"cxxbridge_v${cxx_required_version}"
DEPENDS "${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge"
)
endif()
set(
cxxbridge
"${CMAKE_BINARY_DIR}/cxxbridge_v${cxx_required_version}/bin/cxxbridge"
)
endif()
add_library(${crate_name} STATIC)
target_include_directories(
${crate_name}
PUBLIC
$
$
)
target_link_libraries(
${crate_name}
PUBLIC
${ARG_LIBS}
)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/rust")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h"
COMMAND
"${cxxbridge}" --header --output "${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h"
DEPENDS "cxxbridge_v${cxx_required_version}"
COMMENT "Generating rust/cxx.h header"
)
get_filename_component(filename_component ${CXX_BRIDGE_FILE} NAME)
get_filename_component(directory_component ${CXX_BRIDGE_FILE} DIRECTORY)
set(directory "")
if(directory_component)
set(directory "${directory_component}")
endif()
set(cxx_header ${directory}/${filename_component}.h)
set(cxx_source ${directory}/${filename_component}.cc)
set(rust_source_path "${CMAKE_CURRENT_SOURCE_DIR}/${CXX_BRIDGE_FILE}")
file(
MAKE_DIRECTORY
"${CMAKE_CURRENT_BINARY_DIR}/${directory_component}"
)
add_custom_command(
OUTPUT
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}"
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}"
COMMAND
${cxxbridge} ${rust_source_path}
--cfg fbcode_build=false
--header
--output "${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}"
COMMAND
${cxxbridge} ${rust_source_path}
--cfg fbcode_build=false
--output "${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}"
--include "${cxx_header}"
DEPENDS "cxxbridge_v${cxx_required_version}" "${rust_source_path}"
COMMENT "Generating cxx bindings for crate ${crate_name}"
)
target_sources(
${crate_name}
PRIVATE
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_header}"
"${CMAKE_CURRENT_BINARY_DIR}/rust/cxx.h"
"${CMAKE_CURRENT_BINARY_DIR}/${cxx_source}"
)
endfunction()
================================================
FILE: build/fbcode_builder/CMake/fb_py_test_main.py
================================================
#!/usr/bin/env python
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
"""
This file contains the main module code for Python test programs.
"""
import contextlib
import ctypes
import fnmatch
import json
import logging
import optparse
import os
import platform
import re
import sys
import tempfile
import time
import traceback
import unittest
import warnings
from importlib.machinery import PathFinder
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
try:
import coverage
except ImportError:
coverage = None # type: ignore
try:
from importlib.machinery import SourceFileLoader
except ImportError:
SourceFileLoader = None # type: ignore
class get_cpu_instr_counter:
def read(self):
# TODO
return 0
EXIT_CODE_SUCCESS = 0
EXIT_CODE_TEST_FAILURE = 70
class TestStatus:
ABORTED = "FAILURE"
PASSED = "SUCCESS"
FAILED = "FAILURE"
EXPECTED_FAILURE = "SUCCESS"
UNEXPECTED_SUCCESS = "FAILURE"
SKIPPED = "ASSUMPTION_VIOLATION"
class PathMatcher:
def __init__(self, include_patterns, omit_patterns):
self.include_patterns = include_patterns
self.omit_patterns = omit_patterns
def omit(self, path):
"""
Omit iff matches any of the omit_patterns or the include patterns are
not empty and none is matched
"""
path = os.path.realpath(path)
return any(fnmatch.fnmatch(path, p) for p in self.omit_patterns) or (
self.include_patterns
and not any(fnmatch.fnmatch(path, p) for p in self.include_patterns)
)
def include(self, path):
return not self.omit(path)
class DebugWipeFinder(PathFinder):
"""
PEP 302 finder that uses a DebugWipeLoader for all files which do not need
coverage
"""
def __init__(self, matcher):
self.matcher = matcher
def find_spec(self, fullname, path=None, target=None):
spec = super().find_spec(fullname, path=path, target=target)
if spec is None or spec.origin is None:
return None
if not spec.origin.endswith(".py"):
return None
if self.matcher.include(spec.origin):
return None
class PyVarObject(ctypes.Structure):
_fields_ = [
("ob_refcnt", ctypes.c_long),
("ob_type", ctypes.c_void_p),
("ob_size", ctypes.c_ulong),
]
class DebugWipeLoader(SourceFileLoader):
"""
PEP302 loader that zeros out debug information before execution
"""
def get_code(self, fullname):
code = super().get_code(fullname)
if code:
# Ideally we'd do
# code.co_lnotab = b''
# But code objects are READONLY. Not to worry though; we'll
# directly modify CPython's object
code_impl = PyVarObject.from_address(id(code.co_lnotab))
code_impl.ob_size = 0
return code
if isinstance(spec.loader, SourceFileLoader):
spec.loader = DebugWipeLoader(fullname, spec.origin)
return spec
def optimize_for_coverage(cov, include_patterns, omit_patterns):
"""
We get better performance if we zero out debug information for files which
we're not interested in. Only available in CPython 3.3+
"""
matcher = PathMatcher(include_patterns, omit_patterns)
if SourceFileLoader and platform.python_implementation() == "CPython":
sys.meta_path.insert(0, DebugWipeFinder(matcher))
class TeeStream:
def __init__(self, *streams):
self._streams = streams
def write(self, data):
for stream in self._streams:
stream.write(data)
def flush(self):
for stream in self._streams:
stream.flush()
def isatty(self):
return False
class CallbackStream:
def __init__(self, callback, bytes_callback=None, orig=None):
self._callback = callback
self._fileno = orig.fileno() if orig else None
# Python 3 APIs:
# - `encoding` is a string holding the encoding name
# - `errors` is a string holding the error-handling mode for encoding
# - `buffer` should look like an io.BufferedIOBase object
self.errors = orig.errors if orig else None
if bytes_callback:
# those members are only on the io.TextIOWrapper
self.encoding = orig.encoding if orig else "UTF-8"
self.buffer = CallbackStream(bytes_callback, orig=orig)
def write(self, data):
self._callback(data)
def flush(self):
pass
def isatty(self):
return False
def fileno(self):
return self._fileno
class BuckTestResult(unittest.TextTestResult):
"""
Our own TestResult class that outputs data in a format that can be easily
parsed by buck's test runner.
"""
_instr_counter = get_cpu_instr_counter()
def __init__(
self, stream, descriptions, verbosity, show_output, main_program, suite
):
super(BuckTestResult, self).__init__(stream, descriptions, verbosity)
self._main_program = main_program
self._suite = suite
self._results = []
self._current_test = None
self._saved_stdout = sys.stdout
self._saved_stderr = sys.stderr
self._show_output = show_output
def getResults(self):
return self._results
def startTest(self, test):
super(BuckTestResult, self).startTest(test)
# Pass in the real stdout and stderr filenos. We can't really do much
# here to intercept callers who directly operate on these fileno
# objects.
sys.stdout = CallbackStream(
self.addStdout, self.addStdoutBytes, orig=sys.stdout
)
sys.stderr = CallbackStream(
self.addStderr, self.addStderrBytes, orig=sys.stderr
)
self._current_test = test
self._test_start_time = time.time()
self._current_status = TestStatus.ABORTED
self._messages = []
self._stacktrace = None
self._stdout = ""
self._stderr = ""
self._start_instr_count = self._instr_counter.read()
def _find_next_test(self, suite):
"""
Find the next test that has not been run.
"""
for test in suite:
# We identify test suites by test that are iterable (as is done in
# the builtin python test harness). If we see one, recurse on it.
if hasattr(test, "__iter__"):
test = self._find_next_test(test)
# The builtin python test harness sets test references to `None`
# after they have run, so we know we've found the next test up
# if it's not `None`.
if test is not None:
return test
def stopTest(self, test):
sys.stdout = self._saved_stdout
sys.stderr = self._saved_stderr
super(BuckTestResult, self).stopTest(test)
# If a failure occurred during module/class setup, then this "test" may
# actually be a `_ErrorHolder`, which doesn't contain explicit info
# about the upcoming test. Since we really only care about the test
# name field (i.e. `_testMethodName`), we use that to detect an actual
# test cases, and fall back to looking the test up from the suite
# otherwise.
if not hasattr(test, "_testMethodName"):
test = self._find_next_test(self._suite)
result = {
"testCaseName": "{0}.{1}".format(
test.__class__.__module__, test.__class__.__name__
),
"testCase": test._testMethodName,
"type": self._current_status,
"time": int((time.time() - self._test_start_time) * 1000),
"message": os.linesep.join(self._messages),
"stacktrace": self._stacktrace,
"stdOut": self._stdout,
"stdErr": self._stderr,
}
# TestPilot supports an instruction count field.
if "TEST_PILOT" in os.environ:
result["instrCount"] = (
int(self._instr_counter.read() - self._start_instr_count),
)
self._results.append(result)
self._current_test = None
def stopTestRun(self):
cov = self._main_program.get_coverage()
if cov is not None:
self._results.append({"coverage": cov})
@contextlib.contextmanager
def _withTest(self, test):
self.startTest(test)
yield
self.stopTest(test)
def _setStatus(self, test, status, message=None, stacktrace=None):
assert test == self._current_test
self._current_status = status
self._stacktrace = stacktrace
if message is not None:
if message.endswith(os.linesep):
message = message[:-1]
self._messages.append(message)
def setStatus(self, test, status, message=None, stacktrace=None):
# addError() may be called outside of a test if one of the shared
# fixtures (setUpClass/tearDownClass/setUpModule/tearDownModule)
# throws an error.
#
# In this case, create a fake test result to record the error.
if self._current_test is None:
with self._withTest(test):
self._setStatus(test, status, message, stacktrace)
else:
self._setStatus(test, status, message, stacktrace)
def setException(self, test, status, excinfo):
exctype, value, tb = excinfo
self.setStatus(
test,
status,
"{0}: {1}".format(exctype.__name__, value),
"".join(traceback.format_tb(tb)),
)
def addSuccess(self, test):
super(BuckTestResult, self).addSuccess(test)
self.setStatus(test, TestStatus.PASSED)
def addError(self, test, err):
super(BuckTestResult, self).addError(test, err)
self.setException(test, TestStatus.ABORTED, err)
def addFailure(self, test, err):
super(BuckTestResult, self).addFailure(test, err)
self.setException(test, TestStatus.FAILED, err)
def addSkip(self, test, reason):
super(BuckTestResult, self).addSkip(test, reason)
self.setStatus(test, TestStatus.SKIPPED, "Skipped: %s" % (reason,))
def addExpectedFailure(self, test, err):
super(BuckTestResult, self).addExpectedFailure(test, err)
self.setException(test, TestStatus.EXPECTED_FAILURE, err)
def addUnexpectedSuccess(self, test):
super(BuckTestResult, self).addUnexpectedSuccess(test)
self.setStatus(test, TestStatus.UNEXPECTED_SUCCESS, "Unexpected success")
def addStdout(self, val):
self._stdout += val
if self._show_output:
self._saved_stdout.write(val)
self._saved_stdout.flush()
def addStdoutBytes(self, val):
string = val.decode("utf-8", errors="backslashreplace")
self.addStdout(string)
def addStderr(self, val):
self._stderr += val
if self._show_output:
self._saved_stderr.write(val)
self._saved_stderr.flush()
def addStderrBytes(self, val):
string = val.decode("utf-8", errors="backslashreplace")
self.addStderr(string)
class BuckTestRunner(unittest.TextTestRunner):
def __init__(self, main_program, suite, show_output=True, **kwargs):
super(BuckTestRunner, self).__init__(**kwargs)
self.show_output = show_output
self._main_program = main_program
self._suite = suite
def _makeResult(self):
return BuckTestResult(
self.stream,
self.descriptions,
self.verbosity,
self.show_output,
self._main_program,
self._suite,
)
def _format_test_name(test_class, attrname):
return "{0}.{1}.{2}".format(test_class.__module__, test_class.__name__, attrname)
class StderrLogHandler(logging.StreamHandler):
"""
This class is very similar to logging.StreamHandler, except that it
always uses the current sys.stderr object.
StreamHandler caches the current sys.stderr object when it is constructed.
This makes it behave poorly in unit tests, which may replace sys.stderr
with a StringIO buffer during tests. The StreamHandler will continue using
the old sys.stderr object instead of the desired StringIO buffer.
"""
def __init__(self):
logging.Handler.__init__(self)
@property
def stream(self):
return sys.stderr
class RegexTestLoader(unittest.TestLoader):
def __init__(self, regex=None):
self.regex = regex
super(RegexTestLoader, self).__init__()
def getTestCaseNames(self, testCaseClass):
"""
Return a sorted sequence of method names found within testCaseClass
"""
testFnNames = super(RegexTestLoader, self).getTestCaseNames(testCaseClass)
if self.regex is None:
return testFnNames
robj = re.compile(self.regex)
matched = []
for attrname in testFnNames:
fullname = _format_test_name(testCaseClass, attrname)
if robj.search(fullname):
matched.append(attrname)
return matched
class Loader:
suiteClass = unittest.TestSuite
def __init__(self, modules, regex=None):
self.modules = modules
self.regex = regex
def load_all(self):
loader = RegexTestLoader(self.regex)
test_suite = self.suiteClass()
for module_name in self.modules:
__import__(module_name, level=0)
module = sys.modules[module_name]
module_suite = loader.loadTestsFromModule(module)
test_suite.addTest(module_suite)
return test_suite
def load_args(self, args):
loader = RegexTestLoader(self.regex)
suites = []
for arg in args:
suite = loader.loadTestsFromName(arg)
# loadTestsFromName() can only process names that refer to
# individual test functions or modules. It can't process package
# names. If there were no module/function matches, check to see if
# this looks like a package name.
if suite.countTestCases() != 0:
suites.append(suite)
continue
# Load all modules whose name is .
prefix = arg + "."
for module in self.modules:
if module.startswith(prefix):
suite = loader.loadTestsFromName(module)
suites.append(suite)
return loader.suiteClass(suites)
_COVERAGE_INI = """\
[report]
exclude_lines =
pragma: no cover
pragma: nocover
pragma:.*no${PLATFORM}
pragma:.*no${PY_IMPL}${PY_MAJOR}${PY_MINOR}
pragma:.*no${PY_IMPL}${PY_MAJOR}
pragma:.*nopy${PY_MAJOR}
pragma:.*nopy${PY_MAJOR}${PY_MINOR}
"""
class MainProgram:
"""
This class implements the main program. It can be subclassed by
users who wish to customize some parts of the main program.
(Adding additional command line options, customizing test loading, etc.)
"""
DEFAULT_VERBOSITY = 2
def __init__(self, argv):
self.init_option_parser()
self.parse_options(argv)
self.setup_logging()
def init_option_parser(self):
usage = "%prog [options] [TEST] ..."
op = optparse.OptionParser(usage=usage, add_help_option=False)
self.option_parser = op
op.add_option(
"--hide-output",
dest="show_output",
action="store_false",
default=True,
help="Suppress data that tests print to stdout/stderr, and only "
"show it if the test fails.",
)
op.add_option(
"-o",
"--output",
help="Write results to a file in a JSON format to be read by Buck",
)
op.add_option(
"-f",
"--failfast",
action="store_true",
default=False,
help="Stop after the first failure",
)
op.add_option(
"-l",
"--list-tests",
action="store_true",
dest="list",
default=False,
help="List tests and exit",
)
op.add_option(
"-r",
"--regex",
default=None,
help="Regex to apply to tests, to only run those tests",
)
op.add_option(
"--collect-coverage",
action="store_true",
default=False,
help="Collect test coverage information",
)
op.add_option(
"--coverage-include",
default="*",
help='File globs to include in converage (split by ",")',
)
op.add_option(
"--coverage-omit",
default="",
help='File globs to omit from converage (split by ",")',
)
op.add_option(
"--logger",
action="append",
metavar="=",
default=[],
help="Configure log levels for specific logger categories",
)
op.add_option(
"-q",
"--quiet",
action="count",
default=0,
help="Decrease the verbosity (may be specified multiple times)",
)
op.add_option(
"-v",
"--verbosity",
action="count",
default=self.DEFAULT_VERBOSITY,
help="Increase the verbosity (may be specified multiple times)",
)
op.add_option(
"-?", "--help", action="help", help="Show this help message and exit"
)
def parse_options(self, argv):
self.options, self.test_args = self.option_parser.parse_args(argv[1:])
self.options.verbosity -= self.options.quiet
if self.options.collect_coverage and coverage is None:
self.option_parser.error("coverage module is not available")
self.options.coverage_include = self.options.coverage_include.split(",")
if self.options.coverage_omit == "":
self.options.coverage_omit = []
else:
self.options.coverage_omit = self.options.coverage_omit.split(",")
def setup_logging(self):
# Configure the root logger to log at INFO level.
# This is similar to logging.basicConfig(), but uses our
# StderrLogHandler instead of a StreamHandler.
fmt = logging.Formatter("%(pathname)s:%(lineno)s: %(message)s")
log_handler = StderrLogHandler()
log_handler.setFormatter(fmt)
root_logger = logging.getLogger()
root_logger.addHandler(log_handler)
root_logger.setLevel(logging.INFO)
level_names = {
"debug": logging.DEBUG,
"info": logging.INFO,
"warn": logging.WARNING,
"warning": logging.WARNING,
"error": logging.ERROR,
"critical": logging.CRITICAL,
"fatal": logging.FATAL,
}
for value in self.options.logger:
parts = value.rsplit("=", 1)
if len(parts) != 2:
self.option_parser.error(
"--logger argument must be of the "
"form =: %s" % value
)
name = parts[0]
level_name = parts[1].lower()
level = level_names.get(level_name)
if level is None:
self.option_parser.error(
"invalid log level %r for log " "category %s" % (parts[1], name)
)
logging.getLogger(name).setLevel(level)
def create_loader(self):
import __test_modules__
return Loader(__test_modules__.TEST_MODULES, self.options.regex)
def load_tests(self):
loader = self.create_loader()
if self.options.collect_coverage:
self.start_coverage()
include = self.options.coverage_include
omit = self.options.coverage_omit
if include and "*" not in include:
optimize_for_coverage(self.cov, include, omit)
if self.test_args:
suite = loader.load_args(self.test_args)
else:
suite = loader.load_all()
if self.options.collect_coverage:
self.cov.start()
return suite
def get_tests(self, test_suite):
tests = []
for test in test_suite:
if isinstance(test, unittest.TestSuite):
tests.extend(self.get_tests(test))
else:
tests.append(test)
return tests
def run(self):
test_suite = self.load_tests()
if self.options.list:
for test in self.get_tests(test_suite):
method_name = getattr(test, "_testMethodName", "")
name = _format_test_name(test.__class__, method_name)
print(name)
return EXIT_CODE_SUCCESS
else:
result = self.run_tests(test_suite)
if self.options.output is not None:
with open(self.options.output, "w") as f:
json.dump(result.getResults(), f, indent=4, sort_keys=True)
if not result.wasSuccessful():
return EXIT_CODE_TEST_FAILURE
return EXIT_CODE_SUCCESS
def run_tests(self, test_suite):
# Install a signal handler to catch Ctrl-C and display the results
# (but only if running >2.6).
if sys.version_info[0] > 2 or sys.version_info[1] > 6:
unittest.installHandler()
# Run the tests
runner = BuckTestRunner(
self,
test_suite,
verbosity=self.options.verbosity,
show_output=self.options.show_output,
)
result = runner.run(test_suite)
if self.options.collect_coverage and self.options.show_output:
self.cov.stop()
try:
self.cov.report(file=sys.stdout)
except coverage.misc.CoverageException:
print("No lines were covered, potentially restricted by file filters")
return result
def get_abbr_impl(self):
"""Return abbreviated implementation name."""
impl = platform.python_implementation()
if impl == "PyPy":
return "pp"
elif impl == "Jython":
return "jy"
elif impl == "IronPython":
return "ip"
elif impl == "CPython":
return "cp"
else:
raise RuntimeError("unknown python runtime")
def start_coverage(self):
if not self.options.collect_coverage:
return
with tempfile.NamedTemporaryFile("w", delete=False) as coverage_ini:
coverage_ini.write(_COVERAGE_INI)
self._coverage_ini_path = coverage_ini.name
# Keep the original working dir in case tests use os.chdir
self._original_working_dir = os.getcwd()
# for coverage config ignores by platform/python version
os.environ["PLATFORM"] = sys.platform
os.environ["PY_IMPL"] = self.get_abbr_impl()
os.environ["PY_MAJOR"] = str(sys.version_info.major)
os.environ["PY_MINOR"] = str(sys.version_info.minor)
self.cov = coverage.Coverage(
include=self.options.coverage_include,
omit=self.options.coverage_omit,
config_file=coverage_ini.name,
)
self.cov.erase()
self.cov.start()
def get_coverage(self):
if not self.options.collect_coverage:
return None
try:
os.remove(self._coverage_ini_path)
except OSError:
pass # Better to litter than to fail the test
# Switch back to the original working directory.
os.chdir(self._original_working_dir)
result = {}
self.cov.stop()
try:
f = StringIO()
self.cov.report(file=f)
lines = f.getvalue().split("\n")
except coverage.misc.CoverageException:
# Nothing was covered. That's fine by us
return result
# N.B.: the format of the coverage library's output differs
# depending on whether one or more files are in the results
for line in lines[2:]:
if line.strip("-") == "":
break
r = line.split()[0]
analysis = self.cov.analysis2(r)
covString = self.convert_to_diff_cov_str(analysis)
if covString:
result[r] = covString
return result
def convert_to_diff_cov_str(self, analysis):
# Info on the format of analysis:
# http://nedbatchelder.com/code/coverage/api.html
if not analysis:
return None
numLines = max(
analysis[1][-1] if len(analysis[1]) else 0,
analysis[2][-1] if len(analysis[2]) else 0,
analysis[3][-1] if len(analysis[3]) else 0,
)
lines = ["N"] * numLines
for l in analysis[1]:
lines[l - 1] = "C"
for l in analysis[2]:
lines[l - 1] = "X"
for l in analysis[3]:
lines[l - 1] = "U"
return "".join(lines)
def main(argv):
return MainProgram(sys.argv).run()
if __name__ == "__main__":
sys.exit(main(sys.argv))
================================================
FILE: build/fbcode_builder/CMake/fb_py_win_main.c
================================================
// Copyright (c) Facebook, Inc. and its affiliates.
#define WIN32_LEAN_AND_MEAN
#include
#include
#include
#define PATH_SIZE 32768
typedef int (*Py_Main)(int, wchar_t**);
int locate_py_main(int argc, wchar_t** argv) {
/*
* We have to dynamically locate Python3.dll because we may be loading a
* Python native module while running. If that module is built with a
* different Python version, we will end up a DLL import error. To resolve
* this, we can either ship an embedded version of Python with us or
* dynamically look up existing Python distribution installed on user's
* machine. This way, we should be able to get a consistent version of
* Python3.dll and .pyd modules.
*/
HINSTANCE python_dll;
Py_Main pymain;
python_dll =
LoadLibraryExW(L"python3.dll", NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
int returncode = 0;
if (python_dll != NULL) {
pymain = (Py_Main)GetProcAddress(python_dll, "Py_Main");
if (pymain != NULL) {
returncode = (pymain)(argc, argv);
} else {
fprintf(stderr, "error: %d unable to load Py_Main\n", GetLastError());
}
FreeLibrary(python_dll);
} else {
fprintf(stderr, "error: %d unable to locate python3.dll\n", GetLastError());
return 1;
}
return returncode;
}
int wmain() {
/*
* This executable will be prepended to the start of a Python ZIP archive.
* Python will be able to directly execute the ZIP archive, so we simply
* need to tell Py_Main() to run our own file. Duplicate the argument list
* and add our file name to the beginning to tell Python what file to invoke.
*/
wchar_t** pyargv = malloc(sizeof(wchar_t*) * (__argc + 1));
if (!pyargv) {
fprintf(stderr, "error: failed to allocate argument vector\n");
return 1;
}
/* Py_Main wants the wide character version of the argv so we pull those
* values from the global __wargv array that has been prepared by MSVCRT.
*
* In order for the zipapp to run we need to insert an extra argument in
* the front of the argument vector that points to ourselves.
*
* An additional complication is that, depending on who prepared the argument
* string used to start our process, the computed __wargv[0] can be a simple
* shell word like `watchman-wait` which is normally resolved together with
* the PATH by the shell.
* That unresolved path isn't sufficient to start the zipapp on windows;
* we need the fully qualified path.
*
* Given:
* __wargv == {"watchman-wait", "-h"}
*
* we want to pass the following to Py_Main:
*
* {
* "z:\build\watchman\python\watchman-wait.exe",
* "z:\build\watchman\python\watchman-wait.exe",
* "-h"
* }
*/
wchar_t full_path_to_argv0[PATH_SIZE];
DWORD len = GetModuleFileNameW(NULL, full_path_to_argv0, PATH_SIZE);
if (len == 0 ||
len == PATH_SIZE && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
fprintf(
stderr,
"error: %d while retrieving full path to this executable\n",
GetLastError());
return 1;
}
for (int n = 1; n < __argc; ++n) {
pyargv[n + 1] = __wargv[n];
}
pyargv[0] = full_path_to_argv0;
pyargv[1] = full_path_to_argv0;
return locate_py_main(__argc + 1, pyargv);
}
================================================
FILE: build/fbcode_builder/CMake/make_fbpy_archive.py
================================================
#!/usr/bin/env python3
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
import argparse
import collections
import errno
import os
import shutil
import subprocess
import sys
import tempfile
import zipapp
MANIFEST_SEPARATOR = " :: "
MANIFEST_HEADER_V1 = "FBPY_MANIFEST 1\n"
class UsageError(Exception):
def __init__(self, message):
self.message = message
def __str__(self):
return self.message
class BadManifestError(UsageError):
def __init__(self, path, line_num, message):
full_msg = "%s:%s: %s" % (path, line_num, message)
super().__init__(full_msg)
self.path = path
self.line_num = line_num
self.raw_message = message
PathInfo = collections.namedtuple(
"PathInfo", ("src", "dest", "manifest_path", "manifest_line")
)
def parse_manifest(manifest, path_map):
bad_prefix = ".." + os.path.sep
manifest_dir = os.path.dirname(manifest)
with open(manifest, "r") as f:
line_num = 1
line = f.readline()
if line != MANIFEST_HEADER_V1:
raise BadManifestError(
manifest, line_num, "Unexpected manifest file header"
)
for line in f:
line_num += 1
if line.startswith("#"):
continue
line = line.rstrip("\n")
parts = line.split(MANIFEST_SEPARATOR)
if len(parts) != 2:
msg = "line must be of the form SRC %s DEST" % MANIFEST_SEPARATOR
raise BadManifestError(manifest, line_num, msg)
src, dest = parts
dest = os.path.normpath(dest)
if dest.startswith(bad_prefix):
msg = "destination path starts with %s: %s" % (bad_prefix, dest)
raise BadManifestError(manifest, line_num, msg)
if not os.path.isabs(src):
src = os.path.normpath(os.path.join(manifest_dir, src))
if dest in path_map:
prev_info = path_map[dest]
msg = (
"multiple source paths specified for destination "
"path %s. Previous source was %s from %s:%s"
% (
dest,
prev_info.src,
prev_info.manifest_path,
prev_info.manifest_line,
)
)
raise BadManifestError(manifest, line_num, msg)
info = PathInfo(
src=src,
dest=dest,
manifest_path=manifest,
manifest_line=line_num,
)
path_map[dest] = info
def populate_install_tree(inst_dir, path_map):
os.mkdir(inst_dir)
dest_dirs = {"": False}
def make_dest_dir(path):
if path in dest_dirs:
return
parent = os.path.dirname(path)
make_dest_dir(parent)
abs_path = os.path.join(inst_dir, path)
os.mkdir(abs_path)
dest_dirs[path] = False
def install_file(info):
dir_name, base_name = os.path.split(info.dest)
make_dest_dir(dir_name)
if base_name == "__init__.py":
dest_dirs[dir_name] = True
abs_dest = os.path.join(inst_dir, info.dest)
shutil.copy2(info.src, abs_dest)
# Copy all of the destination files
for info in path_map.values():
install_file(info)
# Create __init__ files in any directories that don't have them.
for dir_path, has_init in dest_dirs.items():
if has_init:
continue
init_path = os.path.join(inst_dir, dir_path, "__init__.py")
with open(init_path, "w"):
pass
def build_pex(args, path_map):
"""Create a self executing python binary using the PEX tool
This type of Python binary is more complex as it requires a third-party tool,
but it does support native language extensions (.so/.dll files).
"""
dest_dir = os.path.dirname(args.output)
with tempfile.TemporaryDirectory(prefix="make_fbpy.", dir=dest_dir) as tmpdir:
inst_dir = os.path.join(tmpdir, "tree")
populate_install_tree(inst_dir, path_map)
if os.path.exists(os.path.join(inst_dir, "__main__.py")):
os.rename(
os.path.join(inst_dir, "__main__.py"),
os.path.join(inst_dir, "main.py"),
)
args.main = "main"
tmp_output = os.path.abspath(os.path.join(tmpdir, "output.exe"))
subprocess.check_call(
["pex"]
+ ["--output-file", tmp_output]
+ ["--python", args.python]
+ ["--sources-directory", inst_dir]
+ ["-e", args.main]
)
os.replace(tmp_output, args.output)
def build_zipapp(args, path_map):
"""Create a self executing python binary using Python 3's built-in
zipapp module.
This type of Python binary is relatively simple, as zipapp is part of the
standard library, but it does not support native language extensions
(.so/.dll files).
"""
dest_dir = os.path.dirname(args.output)
with tempfile.TemporaryDirectory(prefix="make_fbpy.", dir=dest_dir) as tmpdir:
inst_dir = os.path.join(tmpdir, "tree")
populate_install_tree(inst_dir, path_map)
tmp_output = os.path.join(tmpdir, "output.exe")
zipapp.create_archive(
inst_dir, target=tmp_output, interpreter=args.python, main=args.main
)
os.replace(tmp_output, args.output)
def create_main_module(args, inst_dir, path_map):
if not args.main:
assert "__main__.py" in path_map
return
dest_path = os.path.join(inst_dir, "__main__.py")
main_module, main_fn = args.main.split(":")
main_contents = """\
#!{python}
if __name__ == "__main__":
import {main_module}
{main_module}.{main_fn}()
""".format(
python=args.python, main_module=main_module, main_fn=main_fn
)
with open(dest_path, "w") as f:
f.write(main_contents)
os.chmod(dest_path, 0o755)
def build_install_dir(args, path_map):
"""Create a directory that contains all of the sources, with a __main__
module to run the program.
"""
# Populate a temporary directory first, then rename to the destination
# location. This ensures that we don't ever leave a halfway-built
# directory behind at the output path if something goes wrong.
dest_dir = os.path.dirname(args.output)
with tempfile.TemporaryDirectory(prefix="make_fbpy.", dir=dest_dir) as tmpdir:
inst_dir = os.path.join(tmpdir, "tree")
populate_install_tree(inst_dir, path_map)
create_main_module(args, inst_dir, path_map)
os.rename(inst_dir, args.output)
def ensure_directory(path):
try:
os.makedirs(path)
except OSError as ex:
if ex.errno != errno.EEXIST:
raise
def install_library(args, path_map):
"""Create an installation directory a python library."""
out_dir = args.output
out_manifest = args.output + ".manifest"
install_dir = args.install_dir
if not install_dir:
install_dir = out_dir
os.makedirs(out_dir)
with open(out_manifest, "w") as manifest:
manifest.write(MANIFEST_HEADER_V1)
for info in path_map.values():
abs_dest = os.path.join(out_dir, info.dest)
ensure_directory(os.path.dirname(abs_dest))
print("copy %r --> %r" % (info.src, abs_dest))
shutil.copy2(info.src, abs_dest)
installed_dest = os.path.join(install_dir, info.dest)
manifest.write("%s%s%s\n" % (installed_dest, MANIFEST_SEPARATOR, info.dest))
def parse_manifests(args):
# Process args.manifest_separator to help support older versions of CMake
if args.manifest_separator:
manifests = []
for manifest_arg in args.manifests:
split_arg = manifest_arg.split(args.manifest_separator)
manifests.extend(split_arg)
args.manifests = manifests
path_map = {}
for manifest in args.manifests:
parse_manifest(manifest, path_map)
return path_map
def check_main_module(args, path_map):
# Translate an empty string in the --main argument to None,
# just to allow the CMake logic to be slightly simpler and pass in an
# empty string when it really wants the default __main__.py module to be
# used.
if args.main == "":
args.main = None
if args.type == "lib-install":
if args.main is not None:
raise UsageError("cannot specify a --main argument with --type=lib-install")
return
main_info = path_map.get("__main__.py")
if args.main:
if main_info is not None:
msg = (
"specified an explicit main module with --main, "
"but the file listing already includes __main__.py"
)
raise BadManifestError(
main_info.manifest_path, main_info.manifest_line, msg
)
parts = args.main.split(":")
if len(parts) != 2:
raise UsageError(
"argument to --main must be of the form MODULE:CALLABLE "
"(received %s)" % (args.main,)
)
else:
if main_info is None:
raise UsageError(
"no main module specified with --main, "
"and no __main__.py module present"
)
BUILD_TYPES = {
"pex": build_pex,
"zipapp": build_zipapp,
"dir": build_install_dir,
"lib-install": install_library,
}
def main():
ap = argparse.ArgumentParser()
ap.add_argument("-o", "--output", required=True, help="The output file path")
ap.add_argument(
"--install-dir",
help="When used with --type=lib-install, this parameter specifies the "
"final location where the library where be installed. This can be "
"used to generate the library in one directory first, when you plan "
"to move or copy it to another final location later.",
)
ap.add_argument(
"--manifest-separator",
help="Split manifest arguments around this separator. This is used "
"to support older versions of CMake that cannot supply the manifests "
"as separate arguments.",
)
ap.add_argument(
"--main",
help="The main module to run, specified as :. "
"This must be specified if and only if the archive does not contain "
"a __main__.py file.",
)
ap.add_argument(
"--python",
help="Explicitly specify the python interpreter to use for the " "executable.",
)
ap.add_argument(
"--type", choices=BUILD_TYPES.keys(), help="The type of output to build."
)
ap.add_argument(
"manifests",
nargs="+",
help="The manifest files specifying how to construct the archive",
)
args = ap.parse_args()
if args.python is None:
args.python = sys.executable
if args.type is None:
# In the future we might want different default output types
# for different platforms.
args.type = "zipapp"
build_fn = BUILD_TYPES[args.type]
try:
path_map = parse_manifests(args)
check_main_module(args, path_map)
except UsageError as ex:
print("error: %s" % (ex,), file=sys.stderr)
sys.exit(1)
build_fn(args, path_map)
if __name__ == "__main__":
main()
================================================
FILE: build/fbcode_builder/LICENSE
================================================
MIT License
Copyright (c) Facebook, Inc. and its affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: build/fbcode_builder/README.md
================================================
# Easy builds for Facebook projects
This directory contains tools designed to simplify continuous-integration
(and other builds) of Facebook open source projects. In particular, this helps
manage builds for cross-project dependencies.
The main entry point is the `getdeps.py` script. This script has several
subcommands, but the most notable is the `build` command. This will download
and build all dependencies for a project, and then build the project itself.
## Deployment
This directory is copied literally into a number of different Facebook open
source repositories. Any change made to code in this directory will be
automatically be replicated by our open source tooling into all GitHub hosted
repositories that use `fbcode_builder`. Typically this directory is copied
into the open source repositories as `build/fbcode_builder/`.
# Project Configuration Files
The `manifests` subdirectory contains configuration files for many different
projects, describing how to build each project. These files also list
dependencies between projects, enabling `getdeps.py` to build all dependencies
for a project before building the project itself.
# Shared CMake utilities
Since this directory is copied into many Facebook open source repositories,
it is also used to help share some CMake utility files across projects. The
`CMake/` subdirectory contains a number of `.cmake` files that are shared by
the CMake-based build systems across several different projects.
# Older Build Scripts
This directory also still contains a handful of older build scripts that
pre-date the current `getdeps.py` build system. Most of the other `.py` files
in this top directory, apart from `getdeps.py` itself, are from this older
build system. This older system is only used by a few remaining projects, and
new projects should generally use the newer `getdeps.py` script, by adding a
new configuration file in the `manifests/` subdirectory.
================================================
FILE: build/fbcode_builder/getdeps/__init__.py
================================================
# pyre-strict
================================================
FILE: build/fbcode_builder/getdeps/builder.py
================================================
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import glob
import json
import os
import os.path
import pathlib
import re
import shutil
import stat
import subprocess
import sys
import typing
from collections.abc import Callable, Sequence
from shlex import quote as shellquote
from .copytree import rmtree_more, simple_copytree
from .dyndeps import create_dyn_dep_munger
from .envfuncs import add_path_entry, Env, path_search
from .fetcher import copy_if_different, is_public_commit
from .runcmd import make_memory_limit_preexec_fn, run_cmd
if typing.TYPE_CHECKING:
from .buildopts import BuildOptions
from .dyndeps import DepBase
from .load import ManifestLoader
from .manifest import ManifestContext, ManifestParser
class BuilderBase:
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str | None,
inst_dir: str,
env: Env | None = None,
final_install_prefix: str | None = None,
) -> None:
self.env: Env = Env()
if env:
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got
# `Env`.
self.env.update(env)
subdir: str | None = manifest.get("build", "subdir", ctx=ctx)
if subdir:
src_dir = os.path.join(src_dir, subdir)
self.patchfile: str | None = manifest.get("build", "patchfile", ctx=ctx)
self.patchfile_opts: str = (
manifest.get("build", "patchfile_opts", ctx=ctx) or ""
)
self.ctx: ManifestContext = ctx
self.src_dir: str = src_dir
self.build_dir: str = build_dir or src_dir
self.inst_dir: str = inst_dir
self.build_opts: BuildOptions = build_opts
self.manifest: ManifestParser = manifest
self.final_install_prefix: str | None = final_install_prefix
self.loader: ManifestLoader = loader
self.dep_manifests: list[ManifestParser] = dep_manifests
self.install_dirs: list[str] = [
loader.get_project_install_dir(m) for m in dep_manifests
]
def _get_cmd_prefix(self) -> list[str]:
if self.build_opts.is_windows():
vcvarsall = self.build_opts.get_vcvars_path()
if vcvarsall is not None:
# Since it sets rather a large number of variables we mildly abuse
# the cmd quoting rules to assemble a command that calls the script
# to prep the environment and then triggers the actual command that
# we wanted to run.
# Due to changes in vscrsall.bat, it now reports an ERRORLEVEL of 1
# even when succeeding. This occurs when an extension is not present.
# To continue, we must ignore the ERRORLEVEL returned. We do this by
# wrapping the call in a batch file that always succeeds.
wrapper = os.path.join(self.build_dir, "succeed.bat")
with open(wrapper, "w") as f:
f.write("@echo off\n")
f.write(f'call "{vcvarsall}" amd64\n')
f.write("set ERRORLEVEL=0\n")
f.write("exit /b 0\n")
return [wrapper, "&&"]
return []
def _check_cmd(self, cmd: list[str], **kwargs: object) -> None:
"""Run the command and abort on failure"""
# pyre-fixme[6]: For 2nd argument expected `Optional[Env]` but got `object`.
# pyre-fixme[6]: For 2nd argument expected `Optional[str]` but got `object`.
# pyre-fixme[6]: For 2nd argument expected `bool` but got `object`.
rc = self._run_cmd(cmd, **kwargs)
if rc != 0:
raise RuntimeError(f"Failure exit code {rc} for command {cmd}")
def _run_cmd(
self,
cmd: list[str],
cwd: str | None = None,
env: Env | None = None,
use_cmd_prefix: bool = True,
allow_fail: bool = False,
preexec_fn: Callable[[], None] | None = None,
) -> int:
if env:
e = self.env.copy()
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got
# `Env`.
e.update(env)
env = e
else:
env = self.env
if use_cmd_prefix:
cmd_prefix = self._get_cmd_prefix()
if cmd_prefix:
cmd = cmd_prefix + cmd
log_file = os.path.join(self.build_dir, "getdeps_build.log")
return run_cmd(
cmd=cmd,
env=env,
cwd=cwd or self.build_dir,
log_file=log_file,
allow_fail=allow_fail,
preexec_fn=preexec_fn,
)
def _reconfigure(self, reconfigure: bool) -> bool:
if self.build_dir is not None:
if not os.path.isdir(self.build_dir):
os.makedirs(self.build_dir)
reconfigure = True
return reconfigure
def _apply_patchfile(self) -> None:
if self.patchfile is None:
return
patched_sentinel_file = pathlib.Path(self.src_dir + "/.getdeps_patched")
if patched_sentinel_file.exists():
return
old_wd = os.getcwd()
os.chdir(self.src_dir)
# Apply patches from the git repo root so paths resolve correctly
# even when src_dir is a subdirectory of the repo.
try:
git_root = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"], text=True
).strip()
os.chdir(git_root)
except subprocess.CalledProcessError:
pass # not a git repo, stay in src_dir
print(f"Patching {self.manifest.name} with {self.patchfile} in {os.getcwd()}")
patchfile = os.path.join(
self.build_opts.fbcode_builder_dir,
"patches",
# pyre-fixme[6]: For 3rd argument expected `Union[PathLike[str], str]`
# but got `Optional[str]`.
self.patchfile,
)
patchcmd = ["git", "apply", "--ignore-space-change"]
if self.patchfile_opts:
patchcmd.append(self.patchfile_opts)
try:
subprocess.check_call(patchcmd + [patchfile])
except subprocess.CalledProcessError:
raise ValueError(f"Failed to apply patch to {self.manifest.name}")
os.chdir(old_wd)
patched_sentinel_file.touch()
def prepare(self, reconfigure: bool) -> None:
print("Preparing %s..." % self.manifest.name)
reconfigure = self._reconfigure(reconfigure)
self._apply_patchfile()
self._prepare(reconfigure=reconfigure)
def debug(self, reconfigure: bool) -> None:
reconfigure = self._reconfigure(reconfigure)
self._apply_patchfile()
self._prepare(reconfigure=reconfigure)
env = self._compute_env()
print("Starting a shell in %s, ^D to exit..." % self.build_dir)
# TODO: print the command to run the build
shell = ["powershell.exe"] if sys.platform == "win32" else ["/bin/sh", "-i"]
self._run_cmd(shell, cwd=self.build_dir, env=env)
def printenv(self, reconfigure: bool) -> None:
"""print the environment in a shell sourcable format"""
reconfigure = self._reconfigure(reconfigure)
self._apply_patchfile()
self._prepare(reconfigure=reconfigure)
env = self._compute_env(env=Env(src={}))
prefix = "export "
sep = ":"
expand = "$"
expandpost = ""
if self.build_opts.is_windows():
prefix = "SET "
sep = ";"
expand = "%"
expandpost = "%"
for k, v in sorted(env.items()):
existing = os.environ.get(k, None)
if k.endswith("PATH") and existing:
v = shellquote(v) + sep + f"{expand}{k}{expandpost}"
else:
v = shellquote(v)
print("%s%s=%s" % (prefix, k, v))
def build(self, reconfigure: bool) -> None:
print("Building %s..." % self.manifest.name)
reconfigure = self._reconfigure(reconfigure)
self._apply_patchfile()
self._prepare(reconfigure=reconfigure)
self._build(reconfigure=reconfigure)
if self.build_opts.free_up_disk:
# don't clean --src-dir=. case as user may want to build again or run tests on the build
if self.src_dir.startswith(self.build_opts.scratch_dir) and os.path.isdir(
self.build_dir
):
if os.path.islink(self.build_dir):
os.remove(self.build_dir)
else:
rmtree_more(self.build_dir)
elif self.build_opts.is_windows():
# On Windows, emit a wrapper script that can be used to run build artifacts
# directly from the build directory, without installing them. On Windows $PATH
# needs to be updated to include all of the directories containing the runtime
# library dependencies in order to run the binaries.
script_path = self.get_dev_run_script_path()
dep_munger = create_dyn_dep_munger(
self.build_opts, self._compute_env(), self.install_dirs
)
dep_dirs = self.get_dev_run_extra_path_dirs(dep_munger)
# pyre-fixme[16]: Optional type has no attribute `emit_dev_run_script`.
dep_munger.emit_dev_run_script(script_path, dep_dirs)
@property
def _job_weight_mib(self) -> int:
# This is a hack, but we don't have a "defaults manifest" that we can
# customize per platform.
# TODO: Introduce some sort of defaults config that can select by
# platform, just like manifest contexts.
if sys.platform.startswith("freebsd"):
# clang on FreeBSD is quite memory-efficient.
default_job_weight = 512
else:
# 1.5 GiB is a lot to assume, but it's typical of Facebook-style C++.
# Some manifests are even heavier and should override.
default_job_weight = 1536
return int(
self.manifest.get(
"build", "job_weight_mib", str(default_job_weight), ctx=self.ctx
)
)
@property
def num_jobs(self) -> int:
return self.build_opts.get_num_jobs(self._job_weight_mib)
@property
def memory_limit_preexec_fn(self) -> Callable[[], None] | None:
"""Return a preexec_fn that caps per-process virtual memory.
Uses the same job_weight_mib that controls parallelism, so the memory
limit is consistent with the parallelism budget.
"""
return make_memory_limit_preexec_fn(self._job_weight_mib)
def run_tests(
self,
schedule_type: str,
owner: str | None,
test_filter: str | None,
test_exclude: str | None,
retry: int,
no_testpilot: bool,
timeout: int | None = None,
) -> None:
"""Execute any tests that we know how to run. If they fail,
raise an exception."""
pass
def _prepare(self, reconfigure: bool) -> None:
"""Prepare the build. Useful when need to generate config,
but builder is not the primary build system.
e.g. cargo when called from cmake"""
pass
def _build(self, reconfigure: bool) -> None:
"""Perform the build.
reconfigure will be set to true if the fetcher determined
that the sources have changed in such a way that the build
system needs to regenerate its rules."""
pass
def _compute_env(self, env: Env | None = None) -> Env:
if env is None:
env = self.env
# CMAKE_PREFIX_PATH is only respected when passed through the
# environment, so we construct an appropriate path to pass down
return self.build_opts.compute_env_for_install_dirs(
self.loader,
self.dep_manifests,
self.ctx,
env=env,
manifest=self.manifest,
)
def get_dev_run_script_path(self) -> str:
assert self.build_opts.is_windows()
return os.path.join(self.build_dir, "run.ps1")
def get_dev_run_extra_path_dirs(
self, dep_munger: DepBase | None = None
) -> list[str]:
assert self.build_opts.is_windows()
if dep_munger is None:
dep_munger = create_dyn_dep_munger(
self.build_opts, self._compute_env(), self.install_dirs
)
# pyre-fixme[16]: Optional type has no attribute `compute_dependency_paths`.
return dep_munger.compute_dependency_paths(self.build_dir)
class MakeBuilder(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
build_args: list[str] | None,
install_args: list[str] | None,
test_args: list[str] | None,
) -> None:
super(MakeBuilder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
self.build_args: list[str] = build_args or []
self.install_args: list[str] = install_args or []
self.test_args: list[str] | None = test_args
@property
def _make_binary(self) -> str | None:
return self.manifest.get("build", "make_binary", "make", ctx=self.ctx)
def _get_prefix(self) -> list[str]:
return ["PREFIX=" + self.inst_dir, "prefix=" + self.inst_dir]
def _build(self, reconfigure: bool) -> None:
env = self._compute_env()
# Need to ensure that PREFIX is set prior to install because
# libbpf uses it when generating its pkg-config file.
# The lowercase prefix is used by some projects.
cmd = (
[self._make_binary, "-j%s" % self.num_jobs]
+ self.build_args
+ self._get_prefix()
)
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Optional[str]]`.
self._check_cmd(cmd, env=env)
install_cmd = [self._make_binary] + self.install_args + self._get_prefix()
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Optional[str]]`.
self._check_cmd(install_cmd, env=env)
# bz2's Makefile doesn't install its .so properly
if self.manifest and self.manifest.name == "bz2":
libdir = os.path.join(self.inst_dir, "lib")
srcpattern = os.path.join(self.src_dir, "lib*.so.*")
print(f"copying to {libdir} from {srcpattern}")
for file in glob.glob(srcpattern):
shutil.copy(file, libdir)
def run_tests(
self,
schedule_type: str,
owner: str | None,
test_filter: str | None,
test_exclude: str | None,
retry: int,
no_testpilot: bool,
timeout: int | None = None,
) -> None:
if not self.test_args:
return
env = self._compute_env()
if test_filter:
env["GETDEPS_TEST_FILTER"] = test_filter
else:
env["GETDEPS_TEST_FILTER"] = ""
if retry:
# pyre-fixme[6]: Expected `str` but got `int`.
env["GETDEPS_TEST_RETRY"] = retry
else:
# pyre-fixme[6]: Expected `str` but got `int`.
env["GETDEPS_TEST_RETRY"] = 0
if timeout is not None:
env["GETDEPS_TEST_TIMEOUT"] = str(timeout)
cmd = (
[self._make_binary, "-j%s" % self.num_jobs]
# pyre-fixme[58]: `+` is not supported for operand types
# `list[Optional[str]]` and `Optional[list[str]]`.
+ self.test_args
+ self._get_prefix()
)
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Optional[str]]`.
self._check_cmd(cmd, allow_fail=False, env=env)
class CMakeBootStrapBuilder(MakeBuilder):
def _build(self, reconfigure: bool) -> None:
self._check_cmd(
[
"./bootstrap",
"--prefix=" + self.inst_dir,
f"--parallel={self.num_jobs}",
]
)
super(CMakeBootStrapBuilder, self)._build(reconfigure)
class AutoconfBuilder(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
args: list[str] | None,
conf_env_args: dict[str, list[str]] | None,
) -> None:
super(AutoconfBuilder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
self.args: list[str] = args or []
if (
not build_opts.shared_libs
and "--disable-shared" not in self.args
and "--enable-shared" not in self.args
):
self.args.append("--disable-shared")
self.conf_env_args: dict[str, list[str]] = conf_env_args or {}
@property
def _make_binary(self) -> str | None:
return self.manifest.get("build", "make_binary", "make", ctx=self.ctx)
def _build(self, reconfigure: bool) -> None:
configure_path = os.path.join(self.src_dir, "configure")
autogen_path = os.path.join(self.src_dir, "autogen.sh")
env = self._compute_env()
# Some configure scripts need additional env values passed derived from cmds
for k, cmd_args in self.conf_env_args.items():
out = (
subprocess.check_output(cmd_args, env=dict(env.items()))
.decode("utf-8")
.strip()
)
if out:
env.set(k, out)
if not os.path.exists(configure_path):
print("%s doesn't exist, so reconfiguring" % configure_path)
# This libtoolize call is a bit gross; the issue is that
# `autoreconf` as invoked by libsodium's `autogen.sh` doesn't
# seem to realize that it should invoke libtoolize and then
# error out when the configure script references a libtool
# related symbol.
self._check_cmd(["libtoolize"], cwd=self.src_dir, env=env)
# We generally prefer to call the `autogen.sh` script provided
# by the project on the basis that it may know more than plain
# autoreconf does.
if os.path.exists(autogen_path):
self._check_cmd(["bash", autogen_path], cwd=self.src_dir, env=env)
else:
self._check_cmd(["autoreconf", "-ivf"], cwd=self.src_dir, env=env)
configure_cmd = [configure_path, "--prefix=" + self.inst_dir] + self.args
self._check_cmd(configure_cmd, env=env)
only_install = self.manifest.get("build", "only_install", ctx=self.ctx)
if not only_install or only_install.lower() == "false":
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Optional[str]]`.
self._check_cmd([self._make_binary, "-j%s" % self.num_jobs], env=env)
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Union[str, None, str]]`.
self._check_cmd([self._make_binary, "install"], env=env)
class Iproute2Builder(BuilderBase):
# ./configure --prefix does not work for iproute2.
# Thus, explicitly copy sources from src_dir to build_dir, build,
# and then install to inst_dir using DESTDIR
# lastly, also copy include from build_dir to inst_dir
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
) -> None:
super(Iproute2Builder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
def _build(self, reconfigure: bool) -> None:
configure_path = os.path.join(self.src_dir, "configure")
env = self.env.copy()
self._check_cmd([configure_path], env=env)
shutil.rmtree(self.build_dir)
shutil.copytree(self.src_dir, self.build_dir)
self._check_cmd(["make", "-j%s" % self.num_jobs], env=env)
install_cmd = ["make", "install", "DESTDIR=" + self.inst_dir]
for d in ["include", "lib"]:
if not os.path.isdir(os.path.join(self.inst_dir, d)):
shutil.copytree(
os.path.join(self.build_dir, d), os.path.join(self.inst_dir, d)
)
self._check_cmd(install_cmd, env=env)
class MesonBuilder(BuilderBase):
# MesonBuilder assumes that meson build tool has already been installed on
# the machine.
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
) -> None:
super(MesonBuilder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
def _build(self, reconfigure: bool) -> None:
env = self._compute_env()
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.
meson: str | None = path_search(env, "meson")
if meson is None:
raise Exception("Failed to find Meson")
setup_args = self.manifest.get_section_as_args("meson.setup_args", self.ctx)
# Meson builds typically require setup, compile, and install steps.
# During this setup step we ensure that the static library is built and
# the prefix is empty.
self._check_cmd(
[
meson,
"setup",
]
+ setup_args
+ [
self.build_dir,
self.src_dir,
]
)
# Compile step needs to satisfy the build directory that was previously
# prepared during setup.
self._check_cmd([meson, "compile", "-C", self.build_dir])
# Install step
self._check_cmd(
[meson, "install", "-C", self.build_dir, "--destdir", self.inst_dir]
)
class CMakeBuilder(BuilderBase):
MANUAL_BUILD_SCRIPT = """\
#!{sys.executable}
import argparse
import subprocess
import sys
CMAKE = {cmake!r}
CTEST = {ctest!r}
SRC_DIR = {src_dir!r}
BUILD_DIR = {build_dir!r}
INSTALL_DIR = {install_dir!r}
CMD_PREFIX = {cmd_prefix!r}
CMAKE_ENV = {env_str}
CMAKE_DEFINE_ARGS = {define_args_str}
def get_jobs_argument(num_jobs_arg: int) -> str:
if num_jobs_arg > 0:
return "-j" + str(num_jobs_arg)
import multiprocessing
num_jobs = multiprocessing.cpu_count() // 2
return "-j" + str(num_jobs)
def main():
ap = argparse.ArgumentParser()
ap.add_argument(
"cmake_args",
nargs=argparse.REMAINDER,
help='Any extra arguments after an "--" argument will be passed '
"directly to CMake."
)
ap.add_argument(
"--mode",
choices=["configure", "build", "install", "test"],
default="configure",
help="The mode to run: configure, build, or install. "
"Defaults to configure",
)
ap.add_argument(
"--build",
action="store_const",
const="build",
dest="mode",
help="An alias for --mode=build",
)
ap.add_argument(
"-j",
"--num-jobs",
action="store",
type=int,
default=0,
help="Run the build or tests with the specified number of parallel jobs",
)
ap.add_argument(
"--install",
action="store_const",
const="install",
dest="mode",
help="An alias for --mode=install",
)
ap.add_argument(
"--test",
action="store_const",
const="test",
dest="mode",
help="An alias for --mode=test",
)
args = ap.parse_args()
# Strip off a leading "--" from the additional CMake arguments
if args.cmake_args and args.cmake_args[0] == "--":
args.cmake_args = args.cmake_args[1:]
env = CMAKE_ENV
if args.mode == "configure":
full_cmd = CMD_PREFIX + [CMAKE, SRC_DIR] + CMAKE_DEFINE_ARGS + args.cmake_args
elif args.mode in ("build", "install"):
target = "all" if args.mode == "build" else "install"
full_cmd = CMD_PREFIX + [
CMAKE,
"--build",
BUILD_DIR,
"--target",
target,
"--config",
"{build_type}",
get_jobs_argument(args.num_jobs),
] + args.cmake_args
elif args.mode == "test":
full_cmd = CMD_PREFIX + [
{dev_run_script}CTEST,
"--output-on-failure",
get_jobs_argument(args.num_jobs),
] + args.cmake_args
else:
ap.error("unknown invocation mode: %s" % (args.mode,))
cmd_str = " ".join(full_cmd)
print("Running: %r" % (cmd_str,))
proc = subprocess.run(full_cmd, env=env, cwd=BUILD_DIR)
sys.exit(proc.returncode)
if __name__ == "__main__":
main()
"""
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
defines: dict[str, str] | None,
final_install_prefix: str | None = None,
extra_cmake_defines: dict[str, str] | None = None,
cmake_targets: list[str] | None = None,
) -> None:
super(CMakeBuilder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
final_install_prefix=final_install_prefix,
)
self.defines: dict[str, str] = defines or {}
if extra_cmake_defines:
self.defines.update(extra_cmake_defines)
self.cmake_targets: list[str] = cmake_targets or ["install"]
if build_opts.is_windows():
try:
from .facebook.vcvarsall import extra_vc_cmake_defines
except ImportError:
pass
else:
self.defines.update(extra_vc_cmake_defines)
self.loader = loader
if build_opts.shared_libs:
self.defines["BUILD_SHARED_LIBS"] = "ON"
self.defines["BOOST_LINK_STATIC"] = "OFF"
def _invalidate_cache(self) -> None:
for name in [
"CMakeCache.txt",
"CMakeFiles/CMakeError.log",
"CMakeFiles/CMakeOutput.log",
]:
name = os.path.join(self.build_dir, name)
if os.path.isdir(name):
shutil.rmtree(name)
elif os.path.exists(name):
os.unlink(name)
def _needs_reconfigure(self) -> bool:
for name in ["CMakeCache.txt", "build.ninja"]:
name = os.path.join(self.build_dir, name)
if not os.path.exists(name):
return True
return False
def _write_build_script(self, **kwargs: object) -> None:
# pyre-fixme[16]: `object` has no attribute `items`.
env_lines = [" {!r}: {!r},".format(k, v) for k, v in kwargs["env"].items()]
kwargs["env_str"] = "\n".join(["{"] + env_lines + ["}"])
if self.build_opts.is_windows():
kwargs["dev_run_script"] = '"powershell.exe", {!r}, '.format(
self.get_dev_run_script_path()
)
else:
kwargs["dev_run_script"] = ""
define_arg_lines = ["["]
# pyre-fixme[16]: `object` has no attribute `__iter__`.
for arg in kwargs["define_args"]:
# Replace the CMAKE_INSTALL_PREFIX argument to use the INSTALL_DIR
# variable that we define in the MANUAL_BUILD_SCRIPT code.
if arg.startswith("-DCMAKE_INSTALL_PREFIX="):
value = " {!r}.format(INSTALL_DIR),".format(
"-DCMAKE_INSTALL_PREFIX={}"
)
else:
value = " {!r},".format(arg)
define_arg_lines.append(value)
define_arg_lines.append("]")
kwargs["define_args_str"] = "\n".join(define_arg_lines)
# In order to make it easier for developers to manually run builds for
# CMake-based projects, write out some build scripts that can be used to invoke
# CMake manually.
build_script_path = os.path.join(self.build_dir, "run_cmake.py")
script_contents = self.MANUAL_BUILD_SCRIPT.format(**kwargs)
with open(build_script_path, "wb") as f:
f.write(script_contents.encode())
os.chmod(build_script_path, 0o755)
def _compute_cmake_define_args(self, env: Env) -> list[str]:
defines = {
"CMAKE_INSTALL_PREFIX": self.final_install_prefix or self.inst_dir,
"BUILD_SHARED_LIBS": "OFF",
# Some of the deps (rsocket) default to UBSAN enabled if left
# unspecified. Some of the deps fail to compile in release mode
# due to warning->error promotion. RelWithDebInfo is the happy
# medium.
"CMAKE_BUILD_TYPE": self.build_opts.build_type,
}
if "SANDCASTLE" not in os.environ:
# We sometimes see intermittent ccache related breakages on some
# of the FB internal CI hosts, so we prefer to disable ccache
# when running in that environment.
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got
# `Env`.
ccache = path_search(env, "ccache")
if ccache:
defines["CMAKE_CXX_COMPILER_LAUNCHER"] = ccache
else:
# rocksdb does its own probing for ccache.
# Ensure that it is disabled on sandcastle
env["CCACHE_DISABLE"] = "1"
# Some sandcastle hosts have broken ccache related dirs, and
# even though we've asked for it to be disabled ccache is
# still invoked by rocksdb's cmake.
# Redirect its config directory to somewhere that is guaranteed
# fresh to us, and that won't have any ccache data inside.
env["CCACHE_DIR"] = f"{self.build_opts.scratch_dir}/ccache"
if "GITHUB_ACTIONS" in os.environ and self.build_opts.is_windows():
# GitHub actions: the host has both gcc and msvc installed, and
# the default behavior of cmake is to prefer gcc.
# Instruct cmake that we want it to use cl.exe; this is important
# because Boost prefers cl.exe and the mismatch results in cmake
# with gcc not being able to find boost built with cl.exe.
defines["CMAKE_C_COMPILER"] = "cl.exe"
defines["CMAKE_CXX_COMPILER"] = "cl.exe"
if self.build_opts.is_darwin():
# Try to persuade cmake to set the rpath to match the lib
# dirs of the dependencies. This isn't automatic, and to
# make things more interesting, cmake uses `;` as the path
# separator, so translate the runtime path to something
# that cmake will parse
defines["CMAKE_INSTALL_RPATH"] = ";".join(
# pyre-fixme[16]: Optional type has no attribute `split`.
env.get("DYLD_LIBRARY_PATH", "").split(":")
)
# Tell cmake that we want to set the rpath in the tree
# at build time. Without this the rpath is only set
# at the moment that the binaries are installed. That
# default is problematic for example when using the
# gtest integration in cmake which runs the built test
# executables during the build to discover the set of
# tests.
defines["CMAKE_BUILD_WITH_INSTALL_RPATH"] = "ON"
defines.update(self.defines)
define_args = ["-D%s=%s" % (k, v) for (k, v) in defines.items()]
# if self.build_opts.is_windows():
# define_args += ["-G", "Visual Studio 15 2017 Win64"]
define_args += ["-G", "Ninja"]
return define_args
def _run_include_rewriter(self) -> None:
"""Run include path rewriting on source files before building."""
from .include_rewriter import rewrite_includes_from_manifest
print(f"Rewriting include paths for {self.manifest.name}...")
try:
modified_count = rewrite_includes_from_manifest(
self.manifest, self.ctx, self.src_dir, verbose=True
)
if modified_count > 0:
print(f"Successfully modified {modified_count} files")
else:
print("No files needed modification")
except Exception as e:
print(f"Warning: Include path rewriting failed: {e}")
# Don't fail the build for include rewriting issues
def _build(self, reconfigure: bool) -> None:
# Check if include rewriting is enabled
rewrite_includes: str | None = self.manifest.get(
"build", "rewrite_includes", "false", ctx=self.ctx
)
# pyre-fixme[16]: Optional type has no attribute `lower`.
if rewrite_includes.lower() == "true":
self._run_include_rewriter()
reconfigure = reconfigure or self._needs_reconfigure()
env = self._compute_env()
if not self.build_opts.is_windows() and self.final_install_prefix:
env["DESTDIR"] = self.inst_dir
# Resolve the cmake that we installed
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.
cmake = path_search(env, "cmake")
if cmake is None:
raise Exception("Failed to find CMake")
if self.build_opts.is_windows():
checkdir = self.src_dir
if os.path.exists(checkdir):
children = os.listdir(checkdir)
print(f"Building from source {checkdir} contents: {children}")
else:
print(f"Source {checkdir} not found")
if reconfigure:
define_args = self._compute_cmake_define_args(env)
self._write_build_script(
cmd_prefix=self._get_cmd_prefix(),
cmake=cmake,
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but
# got `Env`.
ctest=path_search(env, "ctest"),
env=env,
define_args=define_args,
src_dir=self.src_dir,
build_dir=self.build_dir,
install_dir=self.inst_dir,
sys=sys,
build_type=self.build_opts.build_type,
)
self._invalidate_cache()
self._check_cmd([cmake, self.src_dir] + define_args, env=env)
self._check_cmd(
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Optional[str]]`.
[cmake, "--build", self.build_dir, "--target"]
+ self.cmake_targets
+ [
"--config",
self.build_opts.build_type,
"-j",
str(self.num_jobs),
],
env=env,
preexec_fn=self.memory_limit_preexec_fn,
)
def _build_targets(self, targets: Sequence[str]) -> None:
"""Build one or more cmake targets in parallel.
Args:
targets: Sequence of target names (strings) to build
"""
if not targets:
return
env = self._compute_env()
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.
cmake = path_search(env, "cmake")
if cmake is None:
raise RuntimeError("unable to find cmake")
# Build all targets in a single cmake invocation for better parallelism
cmd = [
cmake,
"--build",
self.build_dir,
]
# Add all targets
for target in targets:
cmd.extend(["--target", target])
cmd.extend(
# pyre-fixme[6]: For 1st argument expected `Iterable[str]` but got
# `Iterable[Union[str, str, None, str]]`.
[
"--config",
self.build_opts.build_type,
"-j",
str(self.num_jobs),
]
)
self._check_cmd(cmd, env=env, preexec_fn=self.memory_limit_preexec_fn)
def _get_missing_test_executables(
self, test_filter: str | None, env: Env, ctest: str | None
) -> set[str]:
"""Discover which test executables are missing for the given filter.
Returns a set of missing executable basenames (without path)."""
if ctest is None:
return set()
# Run ctest -N (show tests without running) with the filter to see which tests match
cmd = [ctest, "-N"]
if test_filter:
cmd += ["-R", test_filter]
try:
output = subprocess.check_output(
cmd,
env=dict(env.items()),
cwd=self.build_dir,
stderr=subprocess.STDOUT,
text=True,
)
except subprocess.CalledProcessError as e:
# If ctest fails, it might be because executables don't exist yet
# Parse the error output to find the missing executables
output = e.output
# Parse output to find missing executable paths
# Look for lines like "Could not find executable /path/to/test_binary"
missing_executables = set()
for line in output.split("\n"):
match = re.search(r"Could not find executable (.+)", line)
if match:
exe_path = match.group(1)
exe_name = os.path.basename(exe_path)
missing_executables.add(exe_name)
return missing_executables
def run_tests(
self,
schedule_type: str,
owner: str | None,
test_filter: str | None,
test_exclude: str | None,
retry: int,
no_testpilot: bool,
timeout: int | None = None,
) -> None:
env = self._compute_env()
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.
ctest: str | None = path_search(env, "ctest")
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.
cmake = path_search(env, "cmake")
# Build only the missing test executables needed for the given filter.
# This is especially important for LocalDirFetcher projects (like fboss)
# where the build marker gets removed when building specific cmake targets.
missing_test_executables = self._get_missing_test_executables(
test_filter, env, ctest
)
if missing_test_executables:
sorted_executables = sorted(missing_test_executables)
print(f"Building missing test executables: {', '.join(sorted_executables)}")
# Build all missing executables in one cmake invocation for better parallelism
self._build_targets(sorted_executables)
def require_command(path: str | None, name: str) -> str:
if path is None:
raise RuntimeError("unable to find command `{}`".format(name))
return path
# On Windows, we also need to update $PATH to include the directories that
# contain runtime library dependencies. This is not needed on other platforms
# since CMake will emit RPATH properly in the binary so they can find these
# dependencies.
if self.build_opts.is_windows():
path_entries = self.get_dev_run_extra_path_dirs()
path = env.get("PATH")
if path:
path_entries.insert(0, path)
env["PATH"] = ";".join(path_entries)
# Don't use the cmd_prefix when running tests. This is vcvarsall.bat on
# Windows. vcvarsall.bat is only needed for the build, not tests. It
# unfortunately fails if invoked with a long PATH environment variable when
# running the tests.
use_cmd_prefix = False
def get_property(
test: dict[str, object], propname: str, defval: object = None
) -> object:
"""extracts a named property from a cmake test info json blob.
The properties look like:
[{"name": "WORKING_DIRECTORY"},
{"value": "something"}]
We assume that it is invalid for the same named property to be
listed more than once.
"""
props = test.get("properties", [])
# pyre-fixme[16]: `object` has no attribute `__iter__`.
for p in props:
if p.get("name", None) == propname:
return p.get("value", defval)
return defval
# pyre-fixme[53]: Captured variable `cmake` is not annotated.
# pyre-fixme[53]: Captured variable `env` is not annotated.
def list_tests() -> list[dict[str, object]]:
output = subprocess.check_output(
[require_command(ctest, "ctest"), "--show-only=json-v1"],
env=env,
cwd=self.build_dir,
)
try:
data = json.loads(output.decode("utf-8"))
except ValueError as exc:
raise Exception(
"Failed to decode cmake test info using %s: %s. Output was: %r"
% (ctest, str(exc), output)
)
tests = []
machine_suffix = self.build_opts.host_type.as_tuple_string()
for test in data["tests"]:
working_dir = get_property(test, "WORKING_DIRECTORY")
labels = []
machine_suffix = self.build_opts.host_type.as_tuple_string()
labels.append("tpx-fb-test-type=3")
labels.append("tpx_test_config::buildsystem=getdeps")
labels.append("tpx_test_config::platform={}".format(machine_suffix))
if get_property(test, "DISABLED"):
labels.append("disabled")
command = test["command"]
if working_dir:
command = [
require_command(cmake, "cmake"),
"-E",
"chdir",
working_dir,
] + command
tests.append(
{
"type": "custom",
"target": "%s-%s-getdeps-%s"
% (self.manifest.name, test["name"], machine_suffix),
"command": command,
"labels": labels,
"env": {},
"required_paths": [],
"contacts": [],
"cwd": os.getcwd(),
}
)
return tests
discover_like_continuous = False
if schedule_type == "continuous" or (
schedule_type == "base_retry" and is_public_commit(self.build_opts)
):
discover_like_continuous = True
if discover_like_continuous or schedule_type == "testwarden":
# for continuous and testwarden runs, disabling retry can give up
# better signals for flaky tests.
retry = 0
tpx = None
try:
from .facebook.testinfra import start_run
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got
# `Env`.
tpx = path_search(env, "tpx")
except ImportError:
# internal testinfra not available
pass
if tpx and not no_testpilot:
import os
buck_test_info = list_tests()
buck_test_info_name = os.path.join(self.build_dir, ".buck-test-info.json")
with open(buck_test_info_name, "w") as f:
json.dump(buck_test_info, f)
env.set("http_proxy", "")
env.set("https_proxy", "")
runs = []
with start_run(env["FBSOURCE_HASH"]) as run_id:
testpilot_args = [
tpx,
"--force-local-execution",
"--buck-test-info",
buck_test_info_name,
"--retry=%d" % retry,
"-j=%s" % str(self.num_jobs),
"--print-long-results",
]
if owner:
testpilot_args += ["--contacts", owner]
if env:
testpilot_args.append("--env")
testpilot_args.extend(f"{key}={val}" for key, val in env.items())
if run_id is not None:
testpilot_args += ["--run-id", run_id]
if timeout is not None:
testpilot_args += ["--timeout", str(timeout)]
if test_filter:
testpilot_args += ["--", test_filter]
if schedule_type == "diff":
runs.append(["--collection", "oss-diff", "--purpose", "diff"])
elif discover_like_continuous:
runs.append(
[
"--tag-new-tests",
"--collection",
"oss-continuous",
"--purpose",
"continuous",
]
)
elif schedule_type == "testwarden":
# One run to assess new tests
runs.append(
[
"--tag-new-tests",
"--collection",
"oss-new-test-stress",
"--stress-runs",
"10",
"--purpose",
"stress-run-new-test",
]
)
# And another for existing tests
runs.append(
[
"--tag-new-tests",
"--collection",
"oss-existing-test-stress",
"--stress-runs",
"10",
"--purpose",
"stress-run",
]
)
else:
runs.append([])
for run in runs:
# FIXME: What is this trying to accomplish? Should it fail on first or >=1 errors?
self._run_cmd(
testpilot_args + run,
cwd=self.build_opts.fbcode_builder_dir,
env=env,
use_cmd_prefix=use_cmd_prefix,
)
else:
args = [
require_command(ctest, "ctest"),
"--output-on-failure",
"-j",
str(self.num_jobs),
]
if test_filter:
args += ["-R", test_filter]
if test_exclude:
args += ["--exclude-regex", test_exclude]
if timeout is not None:
args += ["--timeout", str(timeout)]
count: int = 0
retcode: int | None = -1
while count <= retry:
# FIXME: What is this trying to accomplish? Should it fail on first or >=1 errors?
retcode = self._check_cmd(
args, env=env, use_cmd_prefix=use_cmd_prefix, allow_fail=True
)
if retcode == 0:
break
if count == 0:
# Only add this option in the second run.
args += ["--rerun-failed"]
count += 1
if retcode is not None and retcode != 0:
# Allow except clause in getdeps.main to catch and exit gracefully
# This allows non-testpilot runs to fail through the same logic as failed testpilot runs, which may become handy in case if post test processing is needed in the future
raise subprocess.CalledProcessError(retcode, args)
class NinjaBootstrap(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
build_dir: str,
src_dir: str,
inst_dir: str,
) -> None:
super(NinjaBootstrap, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
def _build(self, reconfigure: bool) -> None:
self._check_cmd(
[sys.executable, "configure.py", "--bootstrap"], cwd=self.src_dir
)
src_ninja = os.path.join(self.src_dir, "ninja")
dest_ninja = os.path.join(self.inst_dir, "bin/ninja")
bin_dir = os.path.dirname(dest_ninja)
if not os.path.exists(bin_dir):
os.makedirs(bin_dir)
shutil.copyfile(src_ninja, dest_ninja)
shutil.copymode(src_ninja, dest_ninja)
class OpenSSLBuilder(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
build_dir: str,
src_dir: str,
inst_dir: str,
) -> None:
super(OpenSSLBuilder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
def _build(self, reconfigure: bool) -> None:
configure = os.path.join(self.src_dir, "Configure")
# prefer to resolve the perl that we installed from
# our manifest on windows, but fall back to the system
# path on eg: darwin
env = self.env.copy()
for m in self.dep_manifests:
bindir = os.path.join(self.loader.get_project_install_dir(m), "bin")
add_path_entry(env, "PATH", bindir, append=False)
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.
perl = typing.cast(str, path_search(env, "perl", "perl"))
make_j_args = []
extra_args = []
if self.build_opts.is_windows():
# jom is compatible with nmake, adds the /j argument for parallel build
make = "jom.exe"
make_j_args = ["/j%s" % self.num_jobs]
args = ["VC-WIN64A-masm", "-utf-8"]
# fixes "if multiple CL.EXE write to the same .PDB file, please use /FS"
extra_args = ["/FS"]
elif self.build_opts.is_darwin():
make = "make"
make_j_args = ["-j%s" % self.num_jobs]
args = (
["darwin64-x86_64-cc"]
if not self.build_opts.is_arm()
else ["darwin64-arm64-cc"]
)
elif self.build_opts.is_linux():
make = "make"
make_j_args = ["-j%s" % self.num_jobs]
args = (
["linux-x86_64"] if not self.build_opts.is_arm() else ["linux-aarch64"]
)
else:
raise Exception("don't know how to build openssl for %r" % self.ctx)
self._check_cmd(
[
perl,
configure,
"--prefix=%s" % self.inst_dir,
"--openssldir=%s" % self.inst_dir,
]
+ args
+ [
"enable-static-engine",
"enable-capieng",
"no-makedepend",
"no-unit-test",
"no-tests",
]
+ extra_args
)
# show the config produced
self._check_cmd([perl, "configdata.pm", "--dump"], env=env)
make_build = [make] + make_j_args
self._check_cmd(make_build, env=env)
make_install = [make, "install_sw", "install_ssldirs"]
self._check_cmd(make_install, env=env)
class Boost(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
b2_args: list[str],
) -> None:
children = os.listdir(src_dir)
assert len(children) == 1, "expected a single directory entry: %r" % (children,)
boost_src = children[0]
assert boost_src.startswith("boost")
src_dir = os.path.join(src_dir, children[0])
super(Boost, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
self.b2_args: list[str] = b2_args
def _build(self, reconfigure: bool) -> None:
env = self._compute_env()
linkage: list[str] = ["static"]
if self.build_opts.is_windows() or self.build_opts.shared_libs:
linkage.append("shared")
args = []
if self.build_opts.is_darwin():
clang = subprocess.check_output(["xcrun", "--find", "clang"])
user_config = os.path.join(self.build_dir, "project-config.jam")
with open(user_config, "w") as jamfile:
jamfile.write("using clang : : %s ;\n" % clang.decode().strip())
args.append("--user-config=%s" % user_config)
for link in linkage:
bootstrap_args = self.manifest.get_section_as_args(
"bootstrap.args", self.ctx
)
if self.build_opts.is_windows():
bootstrap = os.path.join(self.src_dir, "bootstrap.bat")
self._check_cmd([bootstrap] + bootstrap_args, cwd=self.src_dir, env=env)
args += ["address-model=64"]
else:
bootstrap = os.path.join(self.src_dir, "bootstrap.sh")
self._check_cmd(
[bootstrap, "--prefix=%s" % self.inst_dir] + bootstrap_args,
cwd=self.src_dir,
env=env,
)
b2 = os.path.join(self.src_dir, "b2")
self._check_cmd(
[
b2,
"-j%s" % self.num_jobs,
"--prefix=%s" % self.inst_dir,
"--builddir=%s" % self.build_dir,
]
+ args
+ self.b2_args
+ [
"link=%s" % link,
"runtime-link=shared",
"variant=release",
"threading=multi",
"debug-symbols=on",
"visibility=global",
"-d2",
"install",
],
cwd=self.src_dir,
env=env,
)
class NopBuilder(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
inst_dir: str,
) -> None:
super(NopBuilder, self).__init__(
loader, dep_manifests, build_opts, ctx, manifest, src_dir, None, inst_dir
)
def build(self, reconfigure: bool) -> None:
print("Installing %s -> %s" % (self.src_dir, self.inst_dir))
parent = os.path.dirname(self.inst_dir)
if not os.path.exists(parent):
os.makedirs(parent)
install_files = self.manifest.get_section_as_ordered_pairs(
"install.files", self.ctx
)
if install_files:
for src_name, dest_name in self.manifest.get_section_as_ordered_pairs(
"install.files", self.ctx
):
# pyre-fixme[6]: For 2nd argument expected `Union[PathLike[str],
# str]` but got `Optional[str]`.
full_dest = os.path.join(self.inst_dir, dest_name)
full_src = os.path.join(self.src_dir, src_name)
dest_parent = os.path.dirname(full_dest)
if not os.path.exists(dest_parent):
os.makedirs(dest_parent)
if os.path.isdir(full_src):
if not os.path.exists(full_dest):
simple_copytree(full_src, full_dest)
else:
shutil.copyfile(full_src, full_dest)
shutil.copymode(full_src, full_dest)
# This is a bit gross, but the mac ninja.zip doesn't
# give ninja execute permissions, so force them on
# for things that look like they live in a bin dir
# pyre-fixme[6]: For 1st argument expected `PathLike[AnyStr]`
# but got `Optional[str]`.
if os.path.dirname(dest_name) == "bin":
st = os.lstat(full_dest)
os.chmod(full_dest, st.st_mode | stat.S_IXUSR)
else:
if not os.path.exists(self.inst_dir):
simple_copytree(self.src_dir, self.inst_dir)
class SetupPyBuilder(BuilderBase):
def _build(self, reconfigure: bool) -> None:
env = self._compute_env()
setup_env = self.manifest.get_section_as_dict("setup-py.env", self.ctx)
for key, value in setup_env.items():
# pyre-fixme[6]: For 2nd argument expected `str` but got `Optional[str]`.
env[key] = value
setup_py_path = os.path.join(self.src_dir, "setup.py")
if not os.path.exists(setup_py_path):
raise RuntimeError(f"setup.py script not found at {setup_py_path}")
self._check_cmd(
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Union[str, None, str]]`.
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got
# `Env`.
[path_search(env, "python3"), setup_py_path, "install"],
cwd=self.src_dir,
env=env,
)
# Create the installation directory if it doesn't exist
os.makedirs(self.inst_dir, exist_ok=True)
# Mark the project as built
with open(os.path.join(self.inst_dir, ".built-by-getdeps"), "w") as f:
f.write("built")
def run_tests(
self,
schedule_type: str,
owner: str | None,
test_filter: str | None,
test_exclude: str | None,
retry: int,
no_testpilot: bool,
timeout: int | None = None,
) -> None:
# setup.py actually no longer has a standard command for running tests.
# Instead we let manifest files specify an arbitrary Python file to run
# as a test.
# Get the test command from the manifest
python_script = self.manifest.get(
"setup-py.test", "python_script", ctx=self.ctx
)
if not python_script:
print(f"No test script specified for {self.manifest.name}")
return
# Run the command
env = self._compute_env()
self._check_cmd(["python3", python_script], cwd=self.src_dir, env=env)
class SqliteBuilder(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
) -> None:
super(SqliteBuilder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
def _build(self, reconfigure: bool) -> None:
for f in ["sqlite3.c", "sqlite3.h", "sqlite3ext.h"]:
src = os.path.join(self.src_dir, f)
dest = os.path.join(self.build_dir, f)
copy_if_different(src, dest)
cmake_lists = """
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(sqlite3 C)
add_library(sqlite3 STATIC sqlite3.c)
# These options are taken from the defaults in Makefile.msc in
# the sqlite distribution
target_compile_definitions(sqlite3 PRIVATE
-DSQLITE_ENABLE_COLUMN_METADATA=1
-DSQLITE_ENABLE_FTS3=1
-DSQLITE_ENABLE_RTREE=1
-DSQLITE_ENABLE_GEOPOLY=1
-DSQLITE_ENABLE_JSON1=1
-DSQLITE_ENABLE_STMTVTAB=1
-DSQLITE_ENABLE_DBPAGE_VTAB=1
-DSQLITE_ENABLE_DBSTAT_VTAB=1
-DSQLITE_INTROSPECTION_PRAGMAS=1
-DSQLITE_ENABLE_DESERIALIZE=1
)
install(TARGETS sqlite3)
install(FILES sqlite3.h sqlite3ext.h DESTINATION include)
"""
with open(os.path.join(self.build_dir, "CMakeLists.txt"), "w") as f:
f.write(cmake_lists)
defines = {
"CMAKE_INSTALL_PREFIX": self.inst_dir,
"BUILD_SHARED_LIBS": "ON" if self.build_opts.shared_libs else "OFF",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
}
define_args = ["-D%s=%s" % (k, v) for (k, v) in defines.items()]
define_args += ["-G", "Ninja"]
env = self._compute_env()
# Resolve the cmake that we installed
# pyre-fixme[6]: For 1st argument expected `Mapping[str, str]` but got `Env`.
cmake = path_search(env, "cmake")
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Optional[str]]`.
self._check_cmd([cmake, self.build_dir] + define_args, env=env)
self._check_cmd(
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Union[str, str, str, str, str, None, str]]`.
[
cmake,
"--build",
self.build_dir,
"--target",
"install",
"--config",
self.build_opts.build_type,
"-j",
str(self.num_jobs),
],
env=env,
)
================================================
FILE: build/fbcode_builder/getdeps/buildopts.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import argparse
import errno
import glob
import ntpath
import os
import subprocess
import sys
import tempfile
import typing
from collections.abc import Mapping
from .copytree import containing_repo_type
from .envfuncs import add_flag, add_path_entry, Env
from .fetcher import get_fbsource_repo_data, homebrew_package_prefix
from .manifest import ContextGenerator
from .platform import get_available_ram, HostType, is_windows
if typing.TYPE_CHECKING:
from .load import ManifestLoader
from .manifest import ManifestContext, ManifestParser
GITBASH_TMP: str = "c:\\tools\\fb.gitbash\\tmp"
def detect_project(path: str) -> tuple[str | None, str | None]:
repo_type, repo_root = containing_repo_type(path)
if repo_type is None:
return None, None
# Look for a .projectid file. If it exists, read the project name from it.
# pyre-fixme[6]: For 1st argument expected `LiteralString` but got `Optional[str]`.
project_id_path = os.path.join(repo_root, ".projectid")
try:
with open(project_id_path, "r") as f:
project_name = f.read().strip()
return repo_root, project_name
except EnvironmentError as ex:
if ex.errno != errno.ENOENT:
raise
return repo_root, None
class BuildOptions:
def __init__(
self,
fbcode_builder_dir: str,
scratch_dir: str,
host_type: HostType,
install_dir: str | None = None,
num_jobs: int = 0,
use_shipit: bool = False,
vcvars_path: str | None = None,
allow_system_packages: bool = False,
lfs_path: str | None = None,
shared_libs: bool = False,
facebook_internal: bool | None = None,
free_up_disk: bool = False,
build_type: str | None = None,
) -> None:
"""fbcode_builder_dir - the path to either the in-fbsource fbcode_builder dir,
or for shipit-transformed repos, the build dir that
has been mapped into that dir.
scratch_dir - a place where we can store repos and build bits.
This path should be stable across runs and ideally
should not be in the repo of the project being built,
but that is ultimately where we generally fall back
for builds outside of FB
install_dir - where the project will ultimately be installed
num_jobs - the level of concurrency to use while building
use_shipit - use real shipit instead of the simple shipit transformer
vcvars_path - Path to external VS toolchain's vsvarsall.bat
shared_libs - whether to build shared libraries
free_up_disk - take extra actions to save runner disk space
build_type - CMAKE_BUILD_TYPE, used by cmake and cargo builders
"""
if not install_dir:
install_dir = os.path.join(scratch_dir, "installed")
self.project_hashes: str | None = None
for p in ["../deps/github_hashes", "../project_hashes"]:
hashes = os.path.join(fbcode_builder_dir, p)
if os.path.exists(hashes):
self.project_hashes = hashes
break
# Detect what repository and project we are being run from.
# pyre-fixme[4]: Attribute must be annotated.
self.repo_root, self.repo_project = detect_project(os.getcwd())
# If we are running from an fbsource repository, set self.fbsource_dir
# to allow the ShipIt-based fetchers to use it.
if self.repo_project == "fbsource":
self.fbsource_dir: str | None = self.repo_root
else:
self.fbsource_dir = None
if facebook_internal is None:
if self.fbsource_dir:
facebook_internal = True
else:
facebook_internal = False
self.facebook_internal: bool = facebook_internal
self.specified_num_jobs: int = num_jobs
self.scratch_dir: str = scratch_dir
self.install_dir: str = install_dir
self.fbcode_builder_dir: str = fbcode_builder_dir
self.host_type: HostType = host_type
self.use_shipit: bool = use_shipit
self.allow_system_packages: bool = allow_system_packages
self.lfs_path: str | None = lfs_path
self.shared_libs: bool = shared_libs
self.free_up_disk: bool = free_up_disk
self.build_type: str | None = build_type
lib_path: str | None = None
if self.is_darwin():
lib_path = "DYLD_LIBRARY_PATH"
elif self.is_linux():
lib_path = "LD_LIBRARY_PATH"
elif self.is_windows():
lib_path = "PATH"
else:
lib_path = None
self.lib_path: str | None = lib_path
if vcvars_path is None and is_windows():
try:
# Allow a site-specific vcvarsall path.
from .facebook.vcvarsall import build_default_vcvarsall
except ImportError:
vcvarsall: list[str] = []
else:
vcvarsall = (
build_default_vcvarsall(self.fbsource_dir)
if self.fbsource_dir is not None
else []
)
# On Windows, the compiler is not available in the PATH by
# default so we need to run the vcvarsall script to populate the
# environment. We use a glob to find some version of this script
# as deployed with Visual Studio.
if len(vcvarsall) == 0:
# check the 64 bit installs
for year in ["2022"]:
vcvarsall += glob.glob(
os.path.join(
os.environ.get("ProgramFiles", "C:\\Program Files"),
"Microsoft Visual Studio",
year,
"*",
"VC",
"Auxiliary",
"Build",
"vcvarsall.bat",
)
)
# then the 32 bit ones
for year in ["2022", "2019", "2017"]:
vcvarsall += glob.glob(
os.path.join(
os.environ["ProgramFiles(x86)"],
"Microsoft Visual Studio",
year,
"*",
"VC",
"Auxiliary",
"Build",
"vcvarsall.bat",
)
)
if len(vcvarsall) == 0:
raise Exception(
"Could not find vcvarsall.bat. Please install Visual Studio."
)
vcvars_path = vcvarsall[0]
print(f"Using vcvarsall.bat from {vcvars_path}", file=sys.stderr)
self.vcvars_path: str | None = vcvars_path
@property
def manifests_dir(self) -> str:
return os.path.join(self.fbcode_builder_dir, "manifests")
def is_darwin(self) -> bool:
return self.host_type.is_darwin()
def is_windows(self) -> bool:
return self.host_type.is_windows()
def is_arm(self) -> bool:
return self.host_type.is_arm()
def get_vcvars_path(self) -> str | None:
return self.vcvars_path
def is_linux(self) -> bool:
return self.host_type.is_linux()
def is_freebsd(self) -> bool:
return self.host_type.is_freebsd()
def get_num_jobs(self, job_weight: int) -> int:
"""Given an estimated job_weight in MiB, compute a reasonable concurrency limit."""
if self.specified_num_jobs:
return self.specified_num_jobs
available_ram = get_available_ram()
import multiprocessing
return max(1, min(multiprocessing.cpu_count(), available_ram // job_weight))
def get_context_generator(
self, host_tuple: str | HostType | None = None
) -> ContextGenerator:
"""Create a manifest ContextGenerator for the specified target platform."""
if host_tuple is None:
host_type = self.host_type
elif isinstance(host_tuple, HostType):
host_type = host_tuple
else:
host_type = HostType.from_tuple_string(host_tuple)
return ContextGenerator(
{
"os": host_type.ostype,
"distro": host_type.distro,
"distro_vers": host_type.distrovers,
"fb": "on" if self.facebook_internal else "off",
"fbsource": "on" if self.fbsource_dir else "off",
"test": "off",
"shared_libs": "on" if self.shared_libs else "off",
}
)
def compute_env_for_install_dirs(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
ctx: ManifestContext,
env: Env | None = None,
manifest: ManifestParser | None = None,
) -> Env: # noqa: C901
if env is not None:
env = env.copy()
else:
env = Env()
env["GETDEPS_BUILD_DIR"] = os.path.join(self.scratch_dir, "build")
env["GETDEPS_INSTALL_DIR"] = self.install_dir
# Python setuptools attempts to discover a local MSVC for
# building Python extensions. On Windows, getdeps already
# supports invoking a vcvarsall prior to compilation.
#
# Tell setuptools to bypass its own search. This fixes a bug
# where setuptools would fail when run from CMake on GitHub
# Actions with the inscrutable message 'error: Microsoft
# Visual C++ 14.0 is required. Get it with "Build Tools for
# Visual Studio"'. I suspect the actual error is that the
# environment or PATH is overflowing.
#
# For extra credit, someone could patch setuptools to
# propagate the actual error message from vcvarsall, because
# often it does not mean Visual C++ is not available.
#
# Related discussions:
# - https://github.com/pypa/setuptools/issues/2028
# - https://github.com/pypa/setuptools/issues/2307
# - https://developercommunity.visualstudio.com/t/error-microsoft-visual-c-140-is-required/409173
# - https://github.com/OpenMS/OpenMS/pull/4779
# - https://github.com/actions/virtual-environments/issues/1484
if self.is_windows() and self.get_vcvars_path():
env["DISTUTILS_USE_SDK"] = "1"
# On macOS we need to set `SDKROOT` when we use clang for system
# header files.
if self.is_darwin() and "SDKROOT" not in env:
sdkroot = subprocess.check_output(["xcrun", "--show-sdk-path"])
env["SDKROOT"] = sdkroot.decode().strip()
if (
self.is_darwin()
and self.allow_system_packages
and self.host_type.get_package_manager() == "homebrew"
and manifest
and manifest.resolved_system_packages
):
# Homebrew packages may not be on the default PATHs
brew_packages = manifest.resolved_system_packages.get("homebrew", [])
for p in brew_packages:
found = self.add_homebrew_package_to_env(p, env)
# Try extra hard to find openssl, needed with homebrew on macOS
if found and p.startswith("openssl"):
candidate = homebrew_package_prefix("openssl@1.1")
# pyre-fixme[6]: For 1st argument expected
# `Union[PathLike[bytes], PathLike[str], bytes, int, str]` but got
# `Optional[str]`.
if os.path.exists(candidate):
# pyre-fixme[6]: For 2nd argument expected `str` but got
# `Optional[str]`.
os.environ["OPENSSL_ROOT_DIR"] = candidate
env["OPENSSL_ROOT_DIR"] = os.environ["OPENSSL_ROOT_DIR"]
if self.fbsource_dir:
env["YARN_YARN_OFFLINE_MIRROR"] = os.path.join(
self.fbsource_dir, "xplat/third-party/yarn/offline-mirror"
)
yarn_exe = "yarn.bat" if self.is_windows() else "yarn"
env["YARN_PATH"] = os.path.join(
# pyre-fixme[6]: For 1st argument expected `LiteralString` but got
# `Optional[str]`.
self.fbsource_dir,
"xplat/third-party/yarn/",
yarn_exe,
)
node_exe = "node-win-x64.exe" if self.is_windows() else "node"
env["NODE_BIN"] = os.path.join(
# pyre-fixme[6]: For 1st argument expected `LiteralString` but got
# `Optional[str]`.
self.fbsource_dir,
"xplat/third-party/node/bin/",
node_exe,
)
env["RUST_VENDORED_CRATES_DIR"] = os.path.join(
# pyre-fixme[6]: For 1st argument expected `LiteralString` but got
# `Optional[str]`.
self.fbsource_dir,
"third-party/rust/vendor",
)
hash_data = get_fbsource_repo_data(self)
env["FBSOURCE_HASH"] = hash_data.hash
env["FBSOURCE_DATE"] = hash_data.date
# reverse as we are prepending to the PATHs
for m in reversed(dep_manifests):
is_direct_dep = (
manifest is not None and m.name in manifest.get_dependencies(ctx)
)
d = loader.get_project_install_dir(m)
if os.path.exists(d):
self.add_prefix_to_env(
d,
env,
append=False,
is_direct_dep=is_direct_dep,
)
# Linux is always system openssl
system_openssl: bool = self.is_linux()
# For other systems lets see if package is requested
if not system_openssl and manifest and manifest.resolved_system_packages:
for _pkg_type, pkgs in manifest.resolved_system_packages.items():
for p in pkgs:
if p.startswith("openssl") or p.startswith("libssl"):
system_openssl = True
break
# Let openssl know to pick up the system certs if present
if system_openssl or "OPENSSL_DIR" in env:
for system_ssl_cfg in ["/etc/pki/tls", "/etc/ssl"]:
if os.path.isdir(system_ssl_cfg):
cert_dir = system_ssl_cfg + "/certs"
if os.path.isdir(cert_dir):
env["SSL_CERT_DIR"] = cert_dir
cert_file = system_ssl_cfg + "/cert.pem"
if os.path.isfile(cert_file):
env["SSL_CERT_FILE"] = cert_file
return env
def add_homebrew_package_to_env(self, package: str, env: Env) -> bool:
prefix = homebrew_package_prefix(package)
if prefix and os.path.exists(prefix):
return self.add_prefix_to_env(
prefix, env, append=False, add_library_path=True
)
return False
def add_prefix_to_env(
self,
d: str,
env: Env,
append: bool = True,
add_library_path: bool = False,
is_direct_dep: bool = False,
) -> bool: # noqa: C901
bindir: str = os.path.join(d, "bin")
found: bool = False
has_pkgconfig: bool = False
pkgconfig: str = os.path.join(d, "lib", "pkgconfig")
if os.path.exists(pkgconfig):
found = True
has_pkgconfig = True
add_path_entry(env, "PKG_CONFIG_PATH", pkgconfig, append=append)
pkgconfig = os.path.join(d, "lib64", "pkgconfig")
if os.path.exists(pkgconfig):
found = True
has_pkgconfig = True
add_path_entry(env, "PKG_CONFIG_PATH", pkgconfig, append=append)
add_path_entry(env, "CMAKE_PREFIX_PATH", d, append=append)
# Tell the thrift compiler about includes it needs to consider
thriftdir: str = os.path.join(d, "include", "thrift-files")
if os.path.exists(thriftdir):
found = True
add_path_entry(env, "THRIFT_INCLUDE_PATH", thriftdir, append=append)
# module detection for python is old fashioned and needs flags
includedir: str = os.path.join(d, "include")
if os.path.exists(includedir):
found = True
ncursesincludedir: str = os.path.join(d, "include", "ncurses")
if os.path.exists(ncursesincludedir):
add_path_entry(env, "C_INCLUDE_PATH", ncursesincludedir, append=append)
add_flag(env, "CPPFLAGS", f"-I{includedir}", append=append)
add_flag(env, "CPPFLAGS", f"-I{ncursesincludedir}", append=append)
elif "/bz2-" in d:
add_flag(env, "CPPFLAGS", f"-I{includedir}", append=append)
# For non-pkgconfig projects Cabal has no way to find the includes or
# libraries, so we provide a set of extra Cabal flags in the env
if not has_pkgconfig and is_direct_dep:
add_flag(
env,
"GETDEPS_CABAL_FLAGS",
f"--extra-include-dirs={includedir}",
append=append,
)
# The thrift compiler's built-in includes are installed directly to the include dir
includethriftdir: str = os.path.join(d, "include", "thrift")
if os.path.exists(includethriftdir):
add_path_entry(env, "THRIFT_INCLUDE_PATH", includedir, append=append)
# Map from FB python manifests to PYTHONPATH
pydir: str = os.path.join(d, "lib", "fb-py-libs")
if os.path.exists(pydir):
found = True
manifest_ext: str = ".manifest"
pymanifestfiles: list[str] = [
f
for f in os.listdir(pydir)
if f.endswith(manifest_ext) and os.path.isfile(os.path.join(pydir, f))
]
for f in pymanifestfiles:
subdir = f[: -len(manifest_ext)]
add_path_entry(
env, "PYTHONPATH", os.path.join(pydir, subdir), append=append
)
# Allow resolving shared objects built earlier (eg: zstd
# doesn't include the full path to the dylib in its linkage
# so we need to give it an assist)
if self.lib_path:
for lib in ["lib", "lib64"]:
libdir: str = os.path.join(d, lib)
if os.path.exists(libdir):
found = True
# pyre-fixme[6]: For 2nd argument expected `str` but got
# `Optional[str]`.
add_path_entry(env, self.lib_path, libdir, append=append)
# module detection for python is old fashioned and needs flags
if "/ncurses-" in d:
add_flag(env, "LDFLAGS", f"-L{libdir}", append=append)
elif "/bz2-" in d:
add_flag(env, "LDFLAGS", f"-L{libdir}", append=append)
if add_library_path:
add_path_entry(env, "LIBRARY_PATH", libdir, append=append)
if not has_pkgconfig and is_direct_dep:
add_flag(
env,
"GETDEPS_CABAL_FLAGS",
f"--extra-lib-dirs={libdir}",
append=append,
)
# Allow resolving binaries (eg: cmake, ninja) and dlls
# built by earlier steps
if os.path.exists(bindir):
found = True
add_path_entry(env, "PATH", bindir, append=append)
# If rustc is present in the `bin` directory, set RUSTC to prevent
# cargo uses the rustc installed in the system.
if self.is_windows():
cargo_path: str = os.path.join(bindir, "cargo.exe")
rustc_path: str = os.path.join(bindir, "rustc.exe")
rustdoc_path: str = os.path.join(bindir, "rustdoc.exe")
else:
cargo_path = os.path.join(bindir, "cargo")
rustc_path = os.path.join(bindir, "rustc")
rustdoc_path = os.path.join(bindir, "rustdoc")
if os.path.isfile(rustc_path):
env["CARGO_BIN"] = cargo_path
env["RUSTC"] = rustc_path
env["RUSTDOC"] = rustdoc_path
openssl_include: str = os.path.join(d, "include", "openssl")
if os.path.isdir(openssl_include) and any(
os.path.isfile(os.path.join(d, "lib", libcrypto))
for libcrypto in ("libcrypto.lib", "libcrypto.so", "libcrypto.a")
):
# This must be the openssl library, let Rust know about it
env["OPENSSL_DIR"] = d
return found
def list_win32_subst_letters() -> dict[str, str]:
output = subprocess.check_output(["subst"]).decode("utf-8")
# The output is a set of lines like: `F:\: => C:\open\some\where`
lines = output.strip().split("\r\n")
mapping: dict[str, str] = {}
for line in lines:
fields = line.split(": => ")
if len(fields) != 2:
continue
letter = fields[0]
path = fields[1]
mapping[letter] = path
return mapping
def find_existing_win32_subst_for_path(
path: str,
subst_mapping: Mapping[str, str],
) -> str | None:
path = ntpath.normcase(ntpath.normpath(path))
for letter, target in subst_mapping.items():
if ntpath.normcase(target) == path:
return letter
return None
def find_unused_drive_letter() -> str | None:
import ctypes
buffer_len = 256
blen = ctypes.c_uint(buffer_len)
rv = ctypes.c_uint()
bufs = ctypes.create_string_buffer(buffer_len)
# pyre-fixme[16]: Module `ctypes` has no attribute `windll`.
rv = ctypes.windll.kernel32.GetLogicalDriveStringsA(blen, bufs)
if rv > buffer_len:
raise Exception("GetLogicalDriveStringsA result too large for buffer")
nul = "\x00".encode("ascii")
used: list[str] = [
drive.decode("ascii")[0] for drive in bufs.raw.strip(nul).split(nul)
]
possible: list[str] = [c for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
available: list[str] = sorted(list(set(possible) - set(used)))
if len(available) == 0:
return None
# Prefer to assign later letters rather than earlier letters
return available[-1]
def map_subst_path(path: str) -> str:
"""find a short drive letter mapping for a path"""
for _attempt in range(0, 24):
drive = find_existing_win32_subst_for_path(
path, subst_mapping=list_win32_subst_letters()
)
if drive:
return drive
available = find_unused_drive_letter()
if available is None:
raise Exception(
(
"unable to make shorter subst mapping for %s; "
"no available drive letters"
)
% path
)
# Try to set up a subst mapping; note that we may be racing with
# other processes on the same host, so this may not succeed.
try:
subprocess.check_call(["subst", "%s:" % available, path])
subst = "%s:\\" % available
print("Mapped scratch dir %s -> %s" % (path, subst), file=sys.stderr)
return subst
except Exception:
print("Failed to map %s -> %s" % (available, path), file=sys.stderr)
raise Exception("failed to set up a subst path for %s" % path)
def _check_host_type(args: argparse.Namespace, host_type: HostType | None) -> HostType:
if host_type is None:
host_tuple_string: str | None = getattr(args, "host_type", None)
if host_tuple_string:
host_type = HostType.from_tuple_string(host_tuple_string)
else:
host_type = HostType()
assert isinstance(host_type, HostType)
return host_type
def setup_build_options(
args: argparse.Namespace, host_type: HostType | None = None
) -> BuildOptions:
"""Create a BuildOptions object based on the arguments"""
fbcode_builder_dir: str = os.path.dirname(
os.path.dirname(os.path.abspath(__file__))
)
scratch_dir: str | None = args.scratch_path
if not scratch_dir:
# TODO: `mkscratch` doesn't currently know how best to place things on
# sandcastle, so whip up something reasonable-ish
if "SANDCASTLE" in os.environ:
if "DISK_TEMP" not in os.environ:
raise Exception(
(
"I need DISK_TEMP to be set in the sandcastle environment "
"so that I can store build products somewhere sane"
)
)
disk_temp: str = os.environ["DISK_TEMP"]
if is_windows():
# force use gitbash tmp dir for windows, as its less likely to have a tmp cleaner
# that removes extracted prior dated source files
os.makedirs(GITBASH_TMP, exist_ok=True)
print(
f"Using {GITBASH_TMP} instead of DISK_TEMP {disk_temp} for scratch dir",
file=sys.stderr,
)
disk_temp = GITBASH_TMP
scratch_dir = os.path.join(disk_temp, "fbcode_builder_getdeps")
if not scratch_dir:
try:
scratch_dir = (
subprocess.check_output(
["mkscratch", "path", "--subdir", "fbcode_builder_getdeps"]
)
.strip()
.decode("utf-8")
)
except OSError as exc:
if exc.errno != errno.ENOENT:
# A legit failure; don't fall back, surface the error
raise
# This system doesn't have mkscratch so we fall back to
# something local.
munged: str = fbcode_builder_dir.replace("Z", "zZ")
for s in ["/", "\\", ":"]:
munged = munged.replace(s, "Z")
if is_windows() and os.path.isdir("c:/open"):
temp: str = "c:/open/scratch"
else:
temp = tempfile.gettempdir()
scratch_dir = os.path.join(temp, "fbcode_builder_getdeps-%s" % munged)
if not is_windows() and os.geteuid() == 0:
# Running as root; in the case where someone runs
# sudo getdeps.py install-system-deps
# and then runs as build without privs, we want to avoid creating
# a scratch dir that the second stage cannot write to.
# So we generate a different path if we are root.
scratch_dir += "-root"
if not os.path.exists(scratch_dir):
os.makedirs(scratch_dir)
if is_windows():
subst = map_subst_path(scratch_dir)
scratch_dir = subst
else:
if not os.path.exists(scratch_dir):
os.makedirs(scratch_dir)
# Make sure we normalize the scratch path. This path is used as part of the hash
# computation for detecting if projects have been updated, so we need to always
# use the exact same string to refer to a given directory.
# But! realpath in some combinations of Windows/Python3 versions can expand the
# drive substitutions on Windows, so avoid that!
if not is_windows():
scratch_dir = os.path.realpath(scratch_dir)
# Save these args passed by the user in an env variable, so it
# can be used while hashing this build.
os.environ["GETDEPS_CMAKE_DEFINES"] = getattr(args, "extra_cmake_defines", "") or ""
host_type = _check_host_type(args, host_type)
build_args: dict[str, object] = {
k: v
for (k, v) in vars(args).items()
if k
in {
"num_jobs",
"use_shipit",
"vcvars_path",
"allow_system_packages",
"lfs_path",
"shared_libs",
"free_up_disk",
"build_type",
}
}
return BuildOptions(
fbcode_builder_dir,
scratch_dir,
host_type,
install_dir=args.install_prefix,
facebook_internal=args.facebook_internal,
# pyre-fixme[6]: For 6th argument expected `Optional[str]` but got `object`.
# pyre-fixme[6]: For 6th argument expected `bool` but got `object`.
# pyre-fixme[6]: For 6th argument expected `int` but got `object`.
**build_args,
)
================================================
FILE: build/fbcode_builder/getdeps/cache.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
class ArtifactCache:
"""The ArtifactCache is a small abstraction that allows caching
named things in some external storage mechanism.
The primary use case is for storing the build products on CI
systems to accelerate the build"""
def download_to_file(self, name: str, dest_file_name: str) -> bool:
"""If `name` exists in the cache, download it and place it
in the specified `dest_file_name` location on the filesystem.
If a transient issue was encountered a TransientFailure shall
be raised.
If `name` doesn't exist in the cache `False` shall be returned.
If `dest_file_name` was successfully updated `True` shall be
returned.
All other conditions shall raise an appropriate exception."""
return False
def upload_from_file(self, name: str, source_file_name: str) -> None:
"""Causes `name` to be populated in the cache by uploading
the contents of `source_file_name` to the storage system.
If a transient issue was encountered a TransientFailure shall
be raised.
If the upload failed for some other reason, an appropriate
exception shall be raised."""
pass
def create_cache() -> ArtifactCache | None:
"""This function is monkey patchable to provide an actual
implementation"""
return None
================================================
FILE: build/fbcode_builder/getdeps/cargo.py
================================================
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import os
import re
import shutil
import sys
import typing
from .builder import BuilderBase
from .copytree import rmtree_more, simple_copytree
if typing.TYPE_CHECKING:
from .buildopts import BuildOptions
from .load import ManifestLoader
from .manifest import ManifestContext, ManifestParser
class CargoBuilder(BuilderBase):
def __init__(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser], # manifests of dependencies
build_opts: BuildOptions,
ctx: ManifestContext,
manifest: ManifestParser,
src_dir: str,
build_dir: str,
inst_dir: str,
build_doc: bool,
workspace_dir: str | None,
manifests_to_build: str | None,
cargo_config_file: str | None,
) -> None:
super(CargoBuilder, self).__init__(
loader,
dep_manifests,
build_opts,
ctx,
manifest,
src_dir,
build_dir,
inst_dir,
)
self.build_doc = build_doc
self.ws_dir: str | None = workspace_dir
# pyre-fixme[8]: Attribute has type `Optional[List[str]]`; used as
# `Union[None, List[str], str]`.
self.manifests_to_build: list[str] | None = (
manifests_to_build and manifests_to_build.split(",")
)
self.loader: ManifestLoader = loader
self.cargo_config_file_subdir: str | None = cargo_config_file
def run_cargo(
self,
install_dirs: list[str],
operation: str,
args: list[str] | None = None,
) -> None:
args = args or []
env = self._compute_env()
# Enable using nightly features with stable compiler
env["RUSTC_BOOTSTRAP"] = "1"
env["LIBZ_SYS_STATIC"] = "1"
cmd = [
"cargo",
operation,
"--workspace",
"-j%s" % self.num_jobs,
] + args
self._check_cmd(cmd, cwd=self.workspace_dir(), env=env)
def build_source_dir(self) -> str:
return os.path.join(self.build_dir, "source")
def workspace_dir(self) -> str:
return os.path.join(self.build_source_dir(), self.ws_dir or "")
def manifest_dir(self, manifest: str) -> str:
return os.path.join(self.build_source_dir(), manifest)
def recreate_dir(self, src: str, dst: str) -> None:
if os.path.isdir(dst):
if os.path.islink(dst):
os.remove(dst)
else:
rmtree_more(dst)
simple_copytree(src, dst)
def recreate_linked_dir(self, src: str, dst: str) -> None:
if os.path.isdir(dst):
if os.path.islink(dst):
os.remove(dst)
elif os.path.isdir(dst):
shutil.rmtree(dst)
os.symlink(src, dst)
def cargo_config_file(self) -> str:
build_source_dir = self.build_dir
if self.cargo_config_file_subdir:
return os.path.join(build_source_dir, self.cargo_config_file_subdir)
else:
return os.path.join(build_source_dir, ".cargo", "config.toml")
def _create_cargo_config(self) -> dict[str, dict[str, str]]:
cargo_config_file = self.cargo_config_file()
cargo_config_dir = os.path.dirname(cargo_config_file)
if not os.path.isdir(cargo_config_dir):
os.mkdir(cargo_config_dir)
dep_to_git = self._resolve_dep_to_git()
if os.path.isfile(cargo_config_file):
with open(cargo_config_file, "r") as f:
print(f"Reading {cargo_config_file}", file=sys.stderr)
cargo_content = f.read()
else:
cargo_content = ""
new_content = cargo_content
if "# Generated by getdeps.py" not in cargo_content:
new_content += """\
# Generated by getdeps.py
[build]
target-dir = '''{}'''
[profile.dev]
debug = false
incremental = false
[profile.release]
opt-level = "{}"
""".format(
self.build_dir.replace("\\", "\\\\"),
"z" if self.build_opts.build_type == "MinSizeRel" else "s",
)
# Point to vendored sources from getdeps manifests
for _dep, git_conf in dep_to_git.items():
if "cargo_vendored_sources" in git_conf:
vendored_dir = git_conf["cargo_vendored_sources"].replace("\\", "\\\\")
override = (
f'[source."{git_conf["repo_url"]}"]\ndirectory = "{vendored_dir}"\n'
)
if override not in cargo_content:
new_content += override
if self.build_opts.fbsource_dir:
# Point to vendored crates.io if possible
try:
from .facebook.rust import vendored_crates
new_content = vendored_crates(
self.build_opts.fbsource_dir, new_content
)
except ImportError:
# This FB internal module isn't shippped to github,
# so just rely on cargo downloading crates on it's own
pass
if new_content != cargo_content:
with open(cargo_config_file, "w") as f:
print(
f"Writing cargo config for {self.manifest.name} to {cargo_config_file}",
file=sys.stderr,
)
f.write(new_content)
return dep_to_git
def _prepare(self, reconfigure: bool) -> None:
build_source_dir = self.build_source_dir()
self.recreate_dir(self.src_dir, build_source_dir)
dep_to_git = self._create_cargo_config()
if self.ws_dir is not None:
self._patchup_workspace(dep_to_git)
def _build(self, reconfigure: bool) -> None:
# _prepare has been run already. Actually do the build
build_source_dir = self.build_source_dir()
build_args = [
"--artifact-dir",
os.path.join(self.inst_dir, "bin"),
"-Zunstable-options",
]
if self.build_opts.build_type != "Debug":
build_args.append("--release")
if self.manifests_to_build is None:
self.run_cargo(
self.install_dirs,
"build",
build_args,
)
else:
# pyre-fixme[16]: Optional type has no attribute `__iter__`.
for manifest in self.manifests_to_build:
self.run_cargo(
self.install_dirs,
"build",
build_args
+ [
"--manifest-path",
self.manifest_dir(manifest),
],
)
self.recreate_linked_dir(
build_source_dir, os.path.join(self.inst_dir, "source")
)
def run_tests(
self,
schedule_type: str,
owner: str | None,
test_filter: str | None,
test_exclude: str | None,
retry: int,
no_testpilot: bool,
timeout: int | None = None,
) -> None:
build_args: list[str] = []
if self.build_opts.build_type != "Debug":
build_args.append("--release")
if test_filter:
filter_args = ["--", test_filter]
else:
filter_args = []
if self.manifests_to_build is None:
self.run_cargo(self.install_dirs, "test", build_args + filter_args)
if self.build_doc and not filter_args:
self.run_cargo(self.install_dirs, "doc", ["--no-deps"])
else:
# pyre-fixme[16]: Optional type has no attribute `__iter__`.
for manifest in self.manifests_to_build:
margs = ["--manifest-path", self.manifest_dir(manifest)]
self.run_cargo(
self.install_dirs, "test", build_args + filter_args + margs
)
if self.build_doc and not filter_args:
self.run_cargo(self.install_dirs, "doc", ["--no-deps"] + margs)
def _patchup_workspace(self, dep_to_git: dict[str, dict[str, str]]) -> None:
"""
This method makes some assumptions about the state of the project and
its cargo dependendies:
1. Crates from cargo dependencies can be extracted from Cargo.toml files
using _extract_crates function. It is using a heuristic so check its
code to understand how it is done.
2. The extracted cargo dependencies crates can be found in the
dependency's install dir using _resolve_crate_to_path function
which again is using a heuristic.
Notice that many things might go wrong here. E.g. if someone depends
on another getdeps crate by writing in their Cargo.toml file:
my-rename-of-crate = { package = "crate", git = "..." }
they can count themselves lucky because the code will raise an
Exception. There might be more cases where the code will silently pass
producing bad results.
"""
workspace_dir = self.workspace_dir()
git_url_to_crates_and_paths = self._resolve_config(dep_to_git)
if git_url_to_crates_and_paths:
patch_cargo = os.path.join(workspace_dir, "Cargo.toml")
if os.path.isfile(patch_cargo):
with open(patch_cargo, "r") as f:
manifest_content = f.read()
else:
manifest_content = ""
new_content = manifest_content
if "[package]" not in manifest_content:
# A fake manifest has to be crated to change the virtual
# manifest into a non-virtual. The virtual manifests are limited
# in many ways and the inability to define patches on them is
# one. Check https://github.com/rust-lang/cargo/issues/4934 to
# see if it is resolved.
null_file = "/dev/null"
if self.build_opts.is_windows():
null_file = "nul"
new_content += f"""
[package]
name = "fake_manifest_of_{self.manifest.name}"
version = "0.0.0"
[lib]
path = "{null_file}"
"""
config: list[str] = []
for git_url, crates_to_patch_path in git_url_to_crates_and_paths.items():
crates_patches = [
'{} = {{ path = "{}" }}'.format(
crate,
crates_to_patch_path[crate].replace("\\", "\\\\"),
)
for crate in sorted(crates_to_patch_path.keys())
]
patch_key = f'[patch."{git_url}"]'
if patch_key not in manifest_content:
config.append(f"\n{patch_key}\n" + "\n".join(crates_patches))
new_content += "\n".join(config)
if new_content != manifest_content:
with open(patch_cargo, "w") as f:
print(
f"writing patch to {patch_cargo}",
file=sys.stderr,
)
f.write(new_content)
def _resolve_config(
self, dep_to_git: dict[str, dict[str, str]]
) -> dict[str, dict[str, str]]:
"""
Returns a configuration to be put inside root Cargo.toml file which
patches the dependencies git code with local getdeps versions.
See https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section
"""
dep_to_crates = self._resolve_dep_to_crates(self.build_source_dir(), dep_to_git)
git_url_to_crates_and_paths: dict[str, dict[str, str]] = {}
for dep_name in sorted(dep_to_git.keys()):
git_conf = dep_to_git[dep_name]
req_crates = sorted(dep_to_crates.get(dep_name, []))
if not req_crates:
continue # nothing to patch, move along
git_url = git_conf.get("repo_url", None)
crate_source_map = git_conf["crate_source_map"]
if git_url and crate_source_map:
crates_to_patch_path = git_url_to_crates_and_paths.get(git_url, {})
for c in req_crates:
if c in crate_source_map and c not in crates_to_patch_path:
# pyre-fixme[6]: For 1st argument expected `Union[slice[Any,
# Any, Any], SupportsIndex]` but got `str`.
crates_to_patch_path[c] = crate_source_map[c]
print(
f"{self.manifest.name}: Patching crate {c} via virtual manifest in {self.workspace_dir()}",
file=sys.stderr,
)
if crates_to_patch_path:
git_url_to_crates_and_paths[git_url] = crates_to_patch_path
return git_url_to_crates_and_paths
def _resolve_dep_to_git(self) -> dict[str, dict[str, str]]:
"""
For each direct dependency of the currently build manifest check if it
is also cargo-builded and if yes then extract it's git configs and
install dir
"""
dependencies = self.manifest.get_dependencies(self.ctx)
if not dependencies:
return {}
dep_to_git: dict[str, dict[str, str]] = {}
for dep in dependencies:
dep_manifest = self.loader.load_manifest(dep)
dep_builder = dep_manifest.get("build", "builder", ctx=self.ctx)
dep_cargo_conf = dep_manifest.get_section_as_dict("cargo", self.ctx)
dep_crate_map = dep_manifest.get_section_as_dict("crate.pathmap", self.ctx)
if (
not (dep_crate_map or dep_cargo_conf)
and dep_builder not in ["cargo"]
or dep == "rust"
):
# This dependency has no cargo rust content so ignore it.
# The "rust" dependency is an exception since it contains the
# toolchain.
continue
git_conf = dep_manifest.get_section_as_dict("git", self.ctx)
if dep != "rust" and "repo_url" not in git_conf:
raise Exception(
f"{dep}: A cargo dependency requires git.repo_url to be defined."
)
if dep_builder == "cargo":
dep_source_dir = self.loader.get_project_install_dir(dep_manifest)
dep_source_dir = os.path.join(dep_source_dir, "source")
else:
fetcher = self.loader.create_fetcher(dep_manifest)
dep_source_dir = fetcher.get_src_dir()
crate_source_map: dict[str, str] = {}
if dep_crate_map:
for crate, subpath in dep_crate_map.items():
if crate not in crate_source_map:
if self.build_opts.is_windows():
# pyre-fixme[16]: Optional type has no attribute `replace`.
subpath = subpath.replace("/", "\\")
crate_path = os.path.join(dep_source_dir, subpath)
print(
f"{self.manifest.name}: Mapped crate {crate} to dep {dep} dir {crate_path}",
file=sys.stderr,
)
crate_source_map[crate] = crate_path
elif dep_cargo_conf:
# We don't know what crates are defined buy the dep, look for them
search_pattern = re.compile('\\[package\\]\nname = "(.*)"')
for crate_root, _, files in os.walk(dep_source_dir):
if "Cargo.toml" in files:
with open(os.path.join(crate_root, "Cargo.toml"), "r") as f:
content = f.read()
match = search_pattern.search(content)
if match:
crate = match.group(1)
if crate:
print(
f"{self.manifest.name}: Discovered crate {crate} in dep {dep} dir {crate_root}",
file=sys.stderr,
)
crate_source_map[crate] = crate_root
# pyre-fixme[6]: For 2nd argument expected `Optional[str]` but got
# `Dict[str, str]`.
git_conf["crate_source_map"] = crate_source_map
if not dep_crate_map and dep_cargo_conf:
dep_cargo_dir = self.loader.get_project_build_dir(dep_manifest)
dep_cargo_dir = os.path.join(dep_cargo_dir, "source")
dep_ws_dir = dep_cargo_conf.get("workspace_dir", None)
if dep_ws_dir:
dep_cargo_dir = os.path.join(dep_cargo_dir, dep_ws_dir)
git_conf["cargo_vendored_sources"] = dep_cargo_dir
# pyre-fixme[6]: For 2nd argument expected `Dict[str, str]` but got
# `Dict[str, Optional[str]]`.
dep_to_git[dep] = git_conf
return dep_to_git
def _resolve_dep_to_crates(
self,
build_source_dir: str,
dep_to_git: dict[str, dict[str, str]],
) -> dict[str, set[str]]:
"""
This function traverse the build_source_dir in search of Cargo.toml
files, extracts the crate names from them using _extract_crates
function and returns a merged result containing crate names per
dependency name from all Cargo.toml files in the project.
"""
if not dep_to_git:
return {} # no deps, so don't waste time traversing files
dep_to_crates: dict[str, set[str]] = {}
# First populate explicit crate paths from dependencies
for name, git_conf in dep_to_git.items():
# pyre-fixme[16]: `str` has no attribute `keys`.
crates = git_conf["crate_source_map"].keys()
if crates:
dep_to_crates.setdefault(name, set()).update(crates)
# Now find from Cargo.tomls
for root, _, files in os.walk(build_source_dir):
for f in files:
if f == "Cargo.toml":
more_dep_to_crates = CargoBuilder._extract_crates_used(
os.path.join(root, f), dep_to_git
)
for dep_name, crates in more_dep_to_crates.items():
existing_crates = dep_to_crates.get(dep_name, set())
for c in crates:
if c not in existing_crates:
print(
f"Patch {self.manifest.name} uses {dep_name} crate {crates}",
file=sys.stderr,
)
existing_crates.add(c)
# pyre-fixme[61]: `name` is undefined, or not always defined.
dep_to_crates.setdefault(name, set()).update(existing_crates)
return dep_to_crates
@staticmethod
def _extract_crates_used(
cargo_toml_file: str,
dep_to_git: dict[str, dict[str, str]],
) -> dict[str, set[str]]:
"""
This functions reads content of provided cargo toml file and extracts
crate names per each dependency. The extraction is done by a heuristic
so it might be incorrect.
"""
deps_to_crates: dict[str, set[str]] = {}
with open(cargo_toml_file, "r") as f:
for line in f.readlines():
if line.startswith("#") or "git = " not in line:
continue # filter out commented lines and ones without git deps
for dep_name, conf in dep_to_git.items():
# Only redirect deps that point to git URLS
if 'git = "{}"'.format(conf["repo_url"]) in line:
pkg_template = ' package = "'
if pkg_template in line:
crate_name, _, _ = line.partition(pkg_template)[
2
].partition('"')
else:
crate_name, _, _ = line.partition("=")
deps_to_crates.setdefault(dep_name, set()).add(
crate_name.strip()
)
return deps_to_crates
def _resolve_crate_to_path(
self,
crate: str,
crate_source_map: dict[str, str],
) -> str:
"""
Tries to find in source_dir by searching a [package]
keyword followed by name = "".
"""
search_pattern = '[package]\nname = "{}"'.format(crate)
for _crate, crate_source_dir in crate_source_map.items():
for crate_root, _, files in os.walk(crate_source_dir):
if "Cargo.toml" in files:
with open(os.path.join(crate_root, "Cargo.toml"), "r") as f:
content = f.read()
if search_pattern in content:
return crate_root
raise Exception(
f"{self.manifest.name}: Failed to find dep crate {crate} in paths {crate_source_map}"
)
================================================
FILE: build/fbcode_builder/getdeps/copytree.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import os
import shutil
import stat
import subprocess
from collections.abc import Callable
from .platform import is_windows
from .runcmd import run_cmd
PREFETCHED_DIRS: set[str] = set()
def containing_repo_type(path: str) -> tuple[str | None, str | None]:
while True:
if os.path.exists(os.path.join(path, ".git")):
return ("git", path)
if os.path.exists(os.path.join(path, ".hg")):
return ("hg", path)
parent = os.path.dirname(path)
if parent == path:
return None, None
path = parent
def find_eden_root(dirpath: str) -> str | None:
"""If the specified directory is inside an EdenFS checkout, returns
the canonical absolute path to the root of that checkout.
Returns None if the specified directory is not in an EdenFS checkout.
"""
if is_windows():
repo_type, repo_root = containing_repo_type(dirpath)
if repo_root is not None:
if os.path.exists(os.path.join(repo_root, ".eden", "config")):
return repo_root
return None
try:
return os.readlink(os.path.join(dirpath, ".eden", "root"))
except OSError:
return None
def prefetch_dir_if_eden(dirpath: str) -> None:
"""After an amend/rebase, Eden may need to fetch a large number
of trees from the servers. The simplistic single threaded walk
performed by copytree makes this more expensive than is desirable
so we help accelerate things by performing a prefetch on the
source directory"""
global PREFETCHED_DIRS
if dirpath in PREFETCHED_DIRS:
return
root = find_eden_root(dirpath)
if root is None:
return
glob = f"{os.path.relpath(dirpath, root).replace(os.sep, '/')}/**"
print(f"Prefetching {glob}")
subprocess.call(["edenfsctl", "prefetch", "--repo", root, glob, "--background"])
PREFETCHED_DIRS.add(dirpath)
def simple_copytree(src_dir: str, dest_dir: str, symlinks: bool = False) -> str:
"""A simple version of shutil.copytree() that can delegate to native tools if faster"""
if is_windows():
os.makedirs(dest_dir, exist_ok=True)
cmd = [
"robocopy.exe",
src_dir,
dest_dir,
# copy directories, including empty ones
"/E",
# Ignore Extra files in destination
"/XX",
# enable parallel copy
"/MT",
# be quiet
"/NFL",
"/NDL",
"/NJH",
"/NJS",
"/NP",
]
if symlinks:
cmd.append("/SL")
# robocopy exits with code 1 if it copied ok, hence allow_fail
# https://learn.microsoft.com/en-us/troubleshoot/windows-server/backup-and-storage/return-codes-used-robocopy-utility
exit_code = run_cmd(cmd, allow_fail=True)
if exit_code > 1:
raise subprocess.CalledProcessError(exit_code, cmd)
return dest_dir
else:
return shutil.copytree(src_dir, dest_dir, symlinks=symlinks)
def _remove_readonly_and_try_again(
func: Callable[..., object],
path: str,
# pyre-fixme[24]: Generic type `type` expects 1 type parameter, use
# `typing.Type[]` to avoid runtime subscripting errors.
exc_info: tuple[type, BaseException, object],
) -> None:
"""
Error handler for shutil.rmtree.
If the error is due to an access error (read only file)
it attempts to add write permission and then retries the operation.
Any other failure propagates.
"""
# exc_info is a tuple (exc_type, exc_value, traceback)
exc_type = exc_info[0]
if exc_type is PermissionError:
os.chmod(path, stat.S_IWRITE)
# Retry the original function (os.remove or os.rmdir)
try:
func(path)
except Exception:
# If it still fails, the original exception from func() will propagate
raise
else:
# If the error is not a PermissionError, re-raise the original exception
raise exc_info[1]
def rmtree_more(path: str) -> None:
"""Wrapper around shutil.rmtree() that makes it remove readonly files as well.
Useful when git on windows decides to make some files readonly on checkout"""
shutil.rmtree(path, onerror=_remove_readonly_and_try_again)
================================================
FILE: build/fbcode_builder/getdeps/dyndeps.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import errno
import glob
import os
import re
import shlex
import shutil
import stat
import subprocess
import sys
import typing
from collections.abc import Generator
from struct import unpack
if typing.TYPE_CHECKING:
from .buildopts import BuildOptions
from .envfuncs import Env
OBJECT_SUBDIRS: tuple[str, ...] = ("bin", "lib", "lib64")
def copyfile(src: str, dest: str) -> None:
shutil.copyfile(src, dest)
shutil.copymode(src, dest)
class DepBase:
def __init__(
self,
buildopts: BuildOptions,
env: Env,
install_dirs: list[str],
strip: bool,
) -> None:
self.buildopts: BuildOptions = buildopts
self.env: Env = env
self.install_dirs: list[str] = install_dirs
self.strip: bool = strip
# Deduplicates dependency processing. Keyed on the library
# destination path.
self.processed_deps: set[str] = set()
self.munged_lib_dir: str = ""
def list_dynamic_deps(self, objfile: str) -> list[str]:
raise RuntimeError("list_dynamic_deps not implemented")
def interesting_dep(self, d: str) -> bool:
return True
# final_install_prefix must be the equivalent path to `destdir` on the
# installed system. For example, if destdir is `/tmp/RANDOM/usr/local' which
# is intended to map to `/usr/local` in the install image, then
# final_install_prefix='/usr/local'.
# If left unspecified, destdir will be used.
def process_deps(
self, destdir: str, final_install_prefix: str | None = None
) -> None:
if self.buildopts.is_windows():
lib_dir = "bin"
else:
lib_dir = "lib"
self.munged_lib_dir = os.path.join(destdir, lib_dir)
final_lib_dir: str = os.path.join(final_install_prefix or destdir, lib_dir)
if not os.path.isdir(self.munged_lib_dir):
os.makedirs(self.munged_lib_dir)
# Look only at the things that got installed in the leaf package,
# which will be the last entry in the install dirs list
inst_dir: str = self.install_dirs[-1]
print("Process deps under %s" % inst_dir, file=sys.stderr)
for dir in OBJECT_SUBDIRS:
src_dir: str = os.path.join(inst_dir, dir)
if not os.path.isdir(src_dir):
continue
dest_dir: str = os.path.join(destdir, dir)
if not os.path.exists(dest_dir):
os.makedirs(dest_dir)
for objfile in self.list_objs_in_dir(src_dir):
print("Consider %s/%s" % (dir, objfile))
dest_obj: str = os.path.join(dest_dir, objfile)
copyfile(os.path.join(src_dir, objfile), dest_obj)
self.munge_in_place(dest_obj, final_lib_dir)
def find_all_dependencies(self, build_dir: str) -> list[str]:
all_deps: set[str] = set()
for objfile in self.list_objs_in_dir(
build_dir, recurse=True, output_prefix=build_dir
):
for d in self.list_dynamic_deps(objfile):
all_deps.add(d)
interesting_deps: set[str] = {d for d in all_deps if self.interesting_dep(d)}
dep_paths: list[str] = []
for dep in interesting_deps:
dep_path: str | None = self.resolve_loader_path(dep)
if dep_path:
dep_paths.append(dep_path)
return dep_paths
def munge_in_place(self, objfile: str, final_lib_dir: str) -> None:
print("Munging %s" % objfile)
for d in self.list_dynamic_deps(objfile):
if not self.interesting_dep(d):
continue
# Resolve this dep: does it exist in any of our installation
# directories? If so, then it is a candidate for processing
dep: str | None = self.resolve_loader_path(d)
if dep:
dest_dep: str = os.path.join(self.munged_lib_dir, os.path.basename(dep))
print("dep: %s -> %s" % (d, dest_dep))
if dest_dep in self.processed_deps:
# A previous dependency with the same name has already
# been installed at dest_dep, so there is no need to copy
# or munge the dependency again.
# TODO: audit that both source paths have the same inode number
pass
else:
self.processed_deps.add(dest_dep)
copyfile(dep, dest_dep)
self.munge_in_place(dest_dep, final_lib_dir)
self.rewrite_dep(objfile, d, dep, dest_dep, final_lib_dir)
if self.strip:
self.strip_debug_info(objfile)
def rewrite_dep(
self,
objfile: str,
depname: str,
old_dep: str,
new_dep: str,
final_lib_dir: str,
) -> None:
raise RuntimeError("rewrite_dep not implemented")
def resolve_loader_path(self, dep: str) -> str | None:
if os.path.isabs(dep):
return dep
d: str = os.path.basename(dep)
for inst_dir in self.install_dirs:
for libdir in OBJECT_SUBDIRS:
candidate: str = os.path.join(inst_dir, libdir, d)
if os.path.exists(candidate):
return candidate
return None
def list_objs_in_dir(
self, dir: str, recurse: bool = False, output_prefix: str = ""
) -> Generator[str, None, None]:
for entry in os.listdir(dir):
entry_path: str = os.path.join(dir, entry)
st: os.stat_result = os.lstat(entry_path)
if stat.S_ISREG(st.st_mode):
if self.is_objfile(entry_path):
relative_result: str = os.path.join(output_prefix, entry)
yield os.path.normcase(relative_result)
elif recurse and stat.S_ISDIR(st.st_mode):
child_prefix: str = os.path.join(output_prefix, entry)
for result in self.list_objs_in_dir(
entry_path, recurse=recurse, output_prefix=child_prefix
):
yield result
def is_objfile(self, objfile: str) -> bool:
return True
def strip_debug_info(self, objfile: str) -> None:
"""override this to define how to remove debug information
from an object file"""
pass
def check_call_verbose(self, args: list[str]) -> None:
print(" ".join(map(shlex.quote, args)))
subprocess.check_call(args)
class WinDeps(DepBase):
def __init__(
self,
buildopts: BuildOptions,
env: Env,
install_dirs: list[str],
strip: bool,
) -> None:
super(WinDeps, self).__init__(buildopts, env, install_dirs, strip)
self.dumpbin: str = self.find_dumpbin()
def find_dumpbin(self) -> str:
# Looking for dumpbin in the following hardcoded paths.
# The registry option to find the install dir doesn't work anymore.
globs: list[str] = [
(
"C:/Program Files/"
"Microsoft Visual Studio/"
"*/*/VC/Tools/"
"MSVC/*/bin/Hostx64/x64/dumpbin.exe"
),
(
"C:/Program Files (x86)/"
"Microsoft Visual Studio/"
"*/*/VC/Tools/"
"MSVC/*/bin/Hostx64/x64/dumpbin.exe"
),
(
"C:/Program Files (x86)/"
"Common Files/"
"Microsoft/Visual C++ for Python/*/"
"VC/bin/dumpbin.exe"
),
("c:/Program Files (x86)/Microsoft Visual Studio */VC/bin/dumpbin.exe"),
(
"C:/Program Files/Microsoft Visual Studio/*/Professional/VC/Tools/MSVC/*/bin/HostX64/x64/dumpbin.exe"
),
]
for pattern in globs:
for exe in glob.glob(pattern):
return exe
raise RuntimeError("could not find dumpbin.exe")
# pyre-fixme[14]: `list_dynamic_deps` overrides method defined in `DepBase`
# inconsistently.
def list_dynamic_deps(self, exe: str) -> list[str]:
deps: list[str] = []
print("Resolve deps for %s" % exe)
output: str = subprocess.check_output(
[self.dumpbin, "/nologo", "/dependents", exe]
).decode("utf-8")
lines: list[str] = output.split("\n")
for line in lines:
m: re.Match[str] | None = re.match("\\s+(\\S+.dll)", line, re.IGNORECASE)
if m:
deps.append(m.group(1).lower())
return deps
def rewrite_dep(
self,
objfile: str,
depname: str,
old_dep: str,
new_dep: str,
final_lib_dir: str,
) -> None:
# We can't rewrite on windows, but we will
# place the deps alongside the exe so that
# they end up in the search path
pass
# These are the Windows system dll, which we don't want to copy while
# packaging.
SYSTEM_DLLS: set[str] = set( # noqa: C405
[
"advapi32.dll",
"dbghelp.dll",
"kernel32.dll",
"msvcp140.dll",
"vcruntime140.dll",
"ws2_32.dll",
"ntdll.dll",
"shlwapi.dll",
]
)
def interesting_dep(self, d: str) -> bool:
if "api-ms-win-crt" in d:
return False
if d in self.SYSTEM_DLLS:
return False
return True
def is_objfile(self, objfile: str) -> bool:
if not os.path.isfile(objfile):
return False
if objfile.lower().endswith(".exe"):
return True
return False
def emit_dev_run_script(self, script_path: str, dep_dirs: list[str]) -> None:
"""Emit a script that can be used to run build artifacts directly from the
build directory, without installing them.
The dep_dirs parameter should be a list of paths that need to be added to $PATH.
This can be computed by calling compute_dependency_paths() or
compute_dependency_paths_fast().
This is only necessary on Windows, which does not have RPATH, and instead
requires the $PATH environment variable be updated in order to find the proper
library dependencies.
"""
contents: str = self._get_dev_run_script_contents(dep_dirs)
with open(script_path, "w") as f:
f.write(contents)
def compute_dependency_paths(self, build_dir: str) -> list[str]:
"""Return a list of all directories that need to be added to $PATH to ensure
that library dependencies can be found correctly. This is computed by scanning
binaries to determine exactly the right list of dependencies.
The compute_dependency_paths_fast() is a alternative function that runs faster
but may return additional extraneous paths.
"""
dep_dirs: set[str] = set()
# Find paths by scanning the binaries.
for dep in self.find_all_dependencies(build_dir):
dep_dirs.add(os.path.dirname(dep))
dep_dirs.update(self.read_custom_dep_dirs(build_dir))
return sorted(dep_dirs)
def compute_dependency_paths_fast(self, build_dir: str) -> list[str]:
"""Similar to compute_dependency_paths(), but rather than actually scanning
binaries, just add all library paths from the specified installation
directories. This is much faster than scanning the binaries, but may result in
more paths being returned than actually necessary.
"""
dep_dirs: set[str] = set()
for inst_dir in self.install_dirs:
for subdir in OBJECT_SUBDIRS:
path: str = os.path.join(inst_dir, subdir)
if os.path.exists(path):
dep_dirs.add(path)
dep_dirs.update(self.read_custom_dep_dirs(build_dir))
return sorted(dep_dirs)
def read_custom_dep_dirs(self, build_dir: str) -> set[str]:
# The build system may also have included libraries from other locations that
# we might not be able to find normally in find_all_dependencies().
# To handle this situation we support reading additional library paths
# from a LIBRARY_DEP_DIRS.txt file that may have been generated in the build
# output directory.
dep_dirs: set[str] = set()
try:
explicit_dep_dirs_path: str = os.path.join(
build_dir, "LIBRARY_DEP_DIRS.txt"
)
with open(explicit_dep_dirs_path, "r") as f:
for line in f.read().splitlines():
dep_dirs.add(line)
except OSError as ex:
if ex.errno != errno.ENOENT:
raise
return dep_dirs
def _get_dev_run_script_contents(self, path_dirs: list[str]) -> str:
path_entries: list[str] = ["$env:PATH"] + path_dirs
path_str: str = ";".join(path_entries)
return """\
$orig_env = $env:PATH
$env:PATH = "{path_str}"
try {{
$cmd_args = $args[1..$args.length]
& $args[0] @cmd_args
}} finally {{
$env:PATH = $orig_env
}}
""".format(
path_str=path_str
)
class ElfDeps(DepBase):
def __init__(
self,
buildopts: BuildOptions,
env: Env,
install_dirs: list[str],
strip: bool,
) -> None:
super(ElfDeps, self).__init__(buildopts, env, install_dirs, strip)
# We need patchelf to rewrite deps, so ensure that it is built...
args: list[str] = [sys.executable, sys.argv[0]]
if buildopts.allow_system_packages:
args.append("--allow-system-packages")
subprocess.check_call(args + ["build", "patchelf"])
# ... and that we know where it lives
patchelf_install: str = os.fsdecode(
subprocess.check_output(args + ["show-inst-dir", "patchelf"]).strip()
)
if not patchelf_install:
# its a system package, so we assume it is in the path
patchelf_install = "patchelf"
else:
patchelf_install = os.path.join(patchelf_install, "bin", "patchelf")
self.patchelf: str = patchelf_install
def list_dynamic_deps(self, objfile: str) -> list[str]:
out: str = (
subprocess.check_output(
[self.patchelf, "--print-needed", objfile], env=dict(self.env.items())
)
.decode("utf-8")
.strip()
)
lines: list[str] = out.split("\n")
return lines
def rewrite_dep(
self,
objfile: str,
depname: str,
old_dep: str,
new_dep: str,
final_lib_dir: str,
) -> None:
final_dep: str = os.path.join(
final_lib_dir,
os.path.relpath(new_dep, self.munged_lib_dir),
)
self.check_call_verbose(
[self.patchelf, "--replace-needed", depname, final_dep, objfile]
)
def is_objfile(self, objfile: str) -> bool:
if not os.path.isfile(objfile):
return False
with open(objfile, "rb") as f:
# https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
magic: bytes = f.read(4)
return magic == b"\x7fELF"
def strip_debug_info(self, objfile: str) -> None:
self.check_call_verbose(["strip", objfile])
# MACH-O magic number
MACH_MAGIC: int = 0xFEEDFACF
class MachDeps(DepBase):
def interesting_dep(self, d: str) -> bool:
if d.startswith("/usr/lib/") or d.startswith("/System/"):
return False
return True
def is_objfile(self, objfile: str) -> bool:
if not os.path.isfile(objfile):
return False
with open(objfile, "rb") as f:
# mach stores the magic number in native endianness,
# so unpack as native here and compare
header: bytes = f.read(4)
if len(header) != 4:
return False
magic: int = unpack("I", header)[0]
return magic == MACH_MAGIC
def list_dynamic_deps(self, objfile: str) -> list[str]:
if not self.interesting_dep(objfile):
return []
out: str = (
subprocess.check_output(
["otool", "-L", objfile], env=dict(self.env.items())
)
.decode("utf-8")
.strip()
)
lines: list[str] = out.split("\n")
deps: list[str] = []
for line in lines:
m: re.Match[str] | None = re.match("\t(\\S+)\\s", line)
if m:
if os.path.basename(m.group(1)) != os.path.basename(objfile):
deps.append(os.path.normcase(m.group(1)))
return deps
def rewrite_dep(
self,
objfile: str,
depname: str,
old_dep: str,
new_dep: str,
final_lib_dir: str,
) -> None:
if objfile.endswith(".dylib"):
# Erase the original location from the id of the shared
# object. It doesn't appear to hurt to retain it, but
# it does look weird, so let's rewrite it to be sure.
self.check_call_verbose(
["install_name_tool", "-id", os.path.basename(objfile), objfile]
)
final_dep: str = os.path.join(
final_lib_dir,
os.path.relpath(new_dep, self.munged_lib_dir),
)
self.check_call_verbose(
["install_name_tool", "-change", depname, final_dep, objfile]
)
def create_dyn_dep_munger(
buildopts: BuildOptions,
env: Env,
install_dirs: list[str],
strip: bool = False,
) -> DepBase | None:
if buildopts.is_linux():
return ElfDeps(buildopts, env, install_dirs, strip)
if buildopts.is_darwin():
return MachDeps(buildopts, env, install_dirs, strip)
if buildopts.is_windows():
return WinDeps(buildopts, env, install_dirs, strip)
if buildopts.is_freebsd():
return ElfDeps(buildopts, env, install_dirs, strip)
return None
================================================
FILE: build/fbcode_builder/getdeps/envfuncs.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import os
import shlex
import sys
from collections.abc import ItemsView, Iterator, KeysView, Mapping, ValuesView
class Env:
def __init__(self, src: Mapping[str, str] | None = None) -> None:
self._dict: dict[str, str] = {}
if src is None:
self.update(os.environ)
else:
self.update(src)
def update(self, src: Mapping[str, str]) -> None:
for k, v in src.items():
self.set(k, v)
def copy(self) -> Env:
return Env(self._dict)
def _key(self, key: str) -> str | None:
# The `str` cast may not appear to be needed, but without it we run
# into issues when passing the environment to subprocess. The main
# issue is that in python2 `os.environ` (which is the initial source
# of data for the environment) uses byte based strings, but this
# project uses `unicode_literals`. `subprocess` will raise an error
# if the environment that it is passed has a mixture of byte and
# unicode strings.
# It is simplest to force everything to be `str` for the sake of
# consistency.
key = str(key)
if sys.platform.startswith("win"):
# Windows env var names are case insensitive but case preserving.
# An implementation of PAR files on windows gets confused if
# the env block contains keys with conflicting case, so make a
# pass over the contents to remove any.
# While this O(n) scan is technically expensive and gross, it
# is practically not a problem because the volume of calls is
# relatively low and the cost of manipulating the env is dwarfed
# by the cost of spawning a process on windows. In addition,
# since the processes that we run are expensive anyway, this
# overhead is not the worst thing to worry about.
for k in list(self._dict.keys()):
if str(k).lower() == key.lower():
return k
elif key in self._dict:
return key
return None
def get(self, key: str, defval: str | None = None) -> str | None:
resolved_key = self._key(key)
if resolved_key is None:
return defval
return self._dict[resolved_key]
def __getitem__(self, key: str) -> str:
val = self.get(key)
if val is None:
raise KeyError(key)
return val
def unset(self, key: str) -> None:
if key is None:
raise KeyError("attempting to unset env[None]")
resolved_key = self._key(key)
if resolved_key:
del self._dict[resolved_key]
def __delitem__(self, key: str) -> None:
self.unset(key)
def __repr__(self) -> str:
return repr(self._dict)
def set(self, key: str, value: str) -> None:
if key is None:
raise KeyError("attempting to assign env[None] = %r" % value)
if value is None:
raise ValueError("attempting to assign env[%s] = None" % key)
# The `str` conversion is important to avoid triggering errors
# with subprocess if we pass in a unicode value; see commentary
# in the `_key` method.
key = str(key)
value = str(value)
# The `unset` call is necessary on windows where the keys are
# case insensitive. Since this dict is case sensitive, simply
# assigning the value to the new key is not sufficient to remove
# the old value. The `unset` call knows how to match keys and
# remove any potential duplicates.
self.unset(key)
self._dict[key] = value
def __setitem__(self, key: str, value: str) -> None:
self.set(key, value)
def __iter__(self) -> Iterator[str]:
return self._dict.__iter__()
def __len__(self) -> int:
return len(self._dict)
def keys(self) -> KeysView[str]:
return self._dict.keys()
def values(self) -> ValuesView[str]:
return self._dict.values()
def items(self) -> ItemsView[str, str]:
return self._dict.items()
def add_path_entry(
env: Env, name: str, item: str, append: bool = True, separator: str = os.pathsep
) -> None:
"""Cause `item` to be added to the path style env var named
`name` held in the `env` dict. `append` specifies whether
the item is added to the end (the default) or should be
prepended if `name` already exists."""
val = env.get(name, "")
if val is not None and len(val) > 0:
val_list = val.split(separator)
else:
val_list = []
if append:
val_list.append(item)
else:
val_list.insert(0, item)
env.set(name, separator.join(val_list))
def add_flag(env: Env, name: str, flag: str, append: bool = True) -> None:
"""Cause `flag` to be added to the CXXFLAGS-style env var named
`name` held in the `env` dict. `append` specifies whether the
flag is added to the end (the default) or should be prepended if
`name` already exists."""
val = shlex.split(env.get(name, "") or "")
if append:
val.append(flag)
else:
val.insert(0, flag)
env.set(name, " ".join(val))
_path_search_cache: dict[object, str | None] = {}
_not_found: object = object()
def tpx_path() -> str:
return "xplat/testinfra/tpx/ctp.tpx"
def path_search(
env: Mapping[str, str], exename: str, defval: str | None = None
) -> str | None:
"""Search for exename in the PATH specified in env.
exename is eg: `ninja` and this function knows to append a .exe
to the end on windows.
Returns the path to the exe if found, or None if either no
PATH is set in env or no executable is found."""
path = env.get("PATH", None)
if path is None:
return defval
# The project hash computation code searches for C++ compilers (g++, clang, etc)
# repeatedly. Cache the result so we don't end up searching for these over and over
# again.
cache_key = (path, exename)
result = _path_search_cache.get(cache_key, _not_found)
if result is _not_found:
result = _perform_path_search(path, exename)
_path_search_cache[cache_key] = result
# pyre-fixme[7]: Expected `Optional[str]` but got `Optional[object]`.
return result
def _perform_path_search(path: str, exename: str) -> str | None:
is_win = sys.platform.startswith("win")
if is_win:
exename = "%s.exe" % exename
for bindir in path.split(os.pathsep):
full_name = os.path.join(bindir, exename)
if os.path.exists(full_name) and os.path.isfile(full_name):
if not is_win and not os.access(full_name, os.X_OK):
continue
return full_name
return None
================================================
FILE: build/fbcode_builder/getdeps/errors.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
class TransientFailure(Exception):
"""Raising this error causes getdeps to return with an error code
that Sandcastle will consider to be a retryable transient
infrastructure error"""
pass
class ManifestNotFound(Exception):
def __init__(self, manifest_name: str) -> None:
super(Exception, self).__init__("Unable to find manifest '%s'" % manifest_name)
================================================
FILE: build/fbcode_builder/getdeps/expr.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import re
import shlex
from collections.abc import Callable
def parse_expr(expr_text: str, valid_variables: set[str]) -> ExprNode:
"""parses the simple criteria expression syntax used in
dependency specifications.
Returns an ExprNode instance that can be evaluated like this:
```
expr = parse_expr("os=windows")
ok = expr.eval({
"os": "windows"
})
```
Whitespace is allowed between tokens. The following terms
are recognized:
KEY = VALUE # Evaluates to True if ctx[KEY] == VALUE
not(EXPR) # Evaluates to True if EXPR evaluates to False
# and vice versa
all(EXPR1, EXPR2, ...) # Evaluates True if all of the supplied
# EXPR's also evaluate True
any(EXPR1, EXPR2, ...) # Evaluates True if any of the supplied
# EXPR's also evaluate True, False if
# none of them evaluated true.
"""
p = Parser(expr_text, valid_variables)
return p.parse()
class ExprNode:
def eval(self, ctx: dict[str, str | None]) -> bool:
return False
class TrueExpr(ExprNode):
def eval(self, ctx: dict[str, str | None]) -> bool:
return True
def __str__(self) -> str:
return "true"
class NotExpr(ExprNode):
def __init__(self, node: ExprNode) -> None:
self._node: ExprNode = node
def eval(self, ctx: dict[str, str | None]) -> bool:
return not self._node.eval(ctx)
def __str__(self) -> str:
return "not(%s)" % self._node
class AllExpr(ExprNode):
def __init__(self, nodes: list[ExprNode]) -> None:
self._nodes: list[ExprNode] = nodes
def eval(self, ctx: dict[str, str | None]) -> bool:
for node in self._nodes:
if not node.eval(ctx):
return False
return True
def __str__(self) -> str:
items: list[str] = []
for node in self._nodes:
items.append(str(node))
return "all(%s)" % ",".join(items)
class AnyExpr(ExprNode):
def __init__(self, nodes: list[ExprNode]) -> None:
self._nodes: list[ExprNode] = nodes
def eval(self, ctx: dict[str, str | None]) -> bool:
for node in self._nodes:
if node.eval(ctx):
return True
return False
def __str__(self) -> str:
items: list[str] = []
for node in self._nodes:
items.append(str(node))
return "any(%s)" % ",".join(items)
class EqualExpr(ExprNode):
def __init__(self, key: str, value: str) -> None:
self._key: str = key
self._value: str = value
def eval(self, ctx: dict[str, str | None]) -> bool:
return ctx.get(self._key) == self._value
def __str__(self) -> str:
return "%s=%s" % (self._key, self._value)
class Parser:
def __init__(self, text: str, valid_variables: set[str]) -> None:
self.text: str = text
self.lex: shlex.shlex = shlex.shlex(text)
self.valid_variables: set[str] = valid_variables
def parse(self) -> ExprNode:
expr = self.top()
garbage = self.lex.get_token()
if garbage != "":
raise Exception(
"Unexpected token %s after EqualExpr in %s" % (garbage, self.text)
)
return expr
def top(self) -> ExprNode:
name = self.ident()
op = self.lex.get_token()
if op == "(":
parsers: dict[str, Callable[[], ExprNode]] = {
"not": self.parse_not,
"any": self.parse_any,
"all": self.parse_all,
}
func = parsers.get(name)
if not func:
raise Exception("invalid term %s in %s" % (name, self.text))
return func()
if op == "=":
if name not in self.valid_variables:
raise Exception("unknown variable %r in expression" % (name,))
# remove shell quote from value so can test things with period in them, e.g "18.04"
token = self.lex.get_token()
if token is None:
raise Exception("unexpected end of expression in %s" % self.text)
unquoted = " ".join(shlex.split(token))
return EqualExpr(name, unquoted)
raise Exception(
"Unexpected token sequence '%s %s' in %s" % (name, op, self.text)
)
def ident(self) -> str:
ident = self.lex.get_token()
if ident is None or not re.match("[a-zA-Z]+", ident):
raise Exception("expected identifier found %s" % ident)
return ident
def parse_not(self) -> NotExpr:
node = self.top()
expr = NotExpr(node)
tok = self.lex.get_token()
if tok != ")":
raise Exception("expected ')' found %s" % tok)
return expr
def parse_any(self) -> AnyExpr:
nodes: list[ExprNode] = []
while True:
nodes.append(self.top())
tok = self.lex.get_token()
if tok == ")":
break
if tok != ",":
raise Exception("expected ',' or ')' but found %s" % tok)
return AnyExpr(nodes)
def parse_all(self) -> AllExpr:
nodes: list[ExprNode] = []
while True:
nodes.append(self.top())
tok = self.lex.get_token()
if tok == ")":
break
if tok != ",":
raise Exception("expected ',' or ')' but found %s" % tok)
return AllExpr(nodes)
================================================
FILE: build/fbcode_builder/getdeps/fetcher.py
================================================
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import annotations
# pyre-strict
import errno
import hashlib
import os
import random
import re
import shlex
import shutil
import stat
import subprocess
import sys
import tarfile
import time
import zipfile
from abc import ABC, abstractmethod
from collections.abc import Iterator
from datetime import datetime
from typing import NamedTuple, TYPE_CHECKING
from urllib.parse import urlparse
from urllib.request import Request, urlopen
from .copytree import prefetch_dir_if_eden
from .envfuncs import Env
from .errors import TransientFailure
from .platform import HostType, is_windows
from .runcmd import run_cmd
if TYPE_CHECKING:
from .buildopts import BuildOptions
from .manifest import ManifestContext, ManifestParser
def file_name_is_cmake_file(file_name: str) -> bool:
file_name = file_name.lower()
base = os.path.basename(file_name)
return (
base.endswith(".cmake")
or base.endswith(".cmake.in")
or base == "cmakelists.txt"
)
class ChangeStatus:
"""Indicates the nature of changes that happened while updating
the source directory. There are two broad uses:
* When extracting archives for third party software we want to
know that we did something (eg: we either extracted code or
we didn't do anything)
* For 1st party code where we use shipit to transform the code,
we want to know if we changed anything so that we can perform
a build, but we generally want to be a little more nuanced
and be able to distinguish between just changing a source file
and whether we might need to reconfigure the build system.
"""
def __init__(self, all_changed: bool = False) -> None:
"""Construct a ChangeStatus object. The default is to create
a status that indicates no changes, but passing all_changed=True
will create one that indicates that everything changed"""
if all_changed:
self.source_files: int = 1
self.make_files: int = 1
else:
self.source_files: int = 0
self.make_files: int = 0
def record_change(self, file_name: str) -> None:
"""Used by the shipit fetcher to record changes as it updates
files in the destination. If the file name might be one used
in the cmake build system that we use for 1st party code, then
record that as a "make file" change. We could broaden this
to match any file used by various build systems, but it is
only really useful for our internal cmake stuff at this time.
If the file isn't a build file and is under the `fbcode_builder`
dir then we don't class that as an interesting change that we
might need to rebuild, so we ignore it.
Otherwise we record the file as a source file change."""
file_name = file_name.lower()
if file_name_is_cmake_file(file_name):
self.make_files += 1
elif "/fbcode_builder/cmake" in file_name:
self.source_files += 1
elif "/fbcode_builder/" not in file_name:
self.source_files += 1
def sources_changed(self) -> bool:
"""Returns true if any source files were changed during
an update operation. This will typically be used to decide
that the build system to be run on the source dir in an
incremental mode"""
return self.source_files > 0
def build_changed(self) -> bool:
"""Returns true if any build files were changed during
an update operation. This will typically be used to decidfe
that the build system should be reconfigured and re-run
as a full build"""
return self.make_files > 0
class Fetcher(ABC):
"""The Fetcher is responsible for fetching and extracting the
sources for project. The Fetcher instance defines where the
extracted data resides and reports this to the consumer via
its `get_src_dir` method."""
def update(self) -> ChangeStatus:
"""Brings the src dir up to date, ideally minimizing
changes so that a subsequent build doesn't over-build.
Returns a ChangeStatus object that helps the caller to
understand the nature of the changes required during
the update."""
return ChangeStatus()
@abstractmethod
def clean(self) -> None:
"""Reverts any changes that might have been made to
the src dir"""
pass
@abstractmethod
def hash(self) -> str:
"""Returns a hash that identifies the version of the code in the
working copy. For a git repo this is commit hash for the working
copy. For other Fetchers this should relate to the version of
the code in the src dir. The intent is that if a manifest
changes the version/rev of a project that the hash be different.
Importantly, this should be computable without actually fetching
the code, as we want this to factor into a hash used to download
a pre-built version of the code, without having to first download
and extract its sources (eg: boost on windows is pretty painful).
"""
pass
@abstractmethod
def get_src_dir(self) -> str:
"""Returns the source directory that the project was
extracted into"""
pass
class LocalDirFetcher:
"""This class exists to override the normal fetching behavior, and
use an explicit user-specified directory for the project sources.
This fetcher cannot update or track changes. It always reports that the
project has changed, forcing it to always be built."""
def __init__(self, path: str) -> None:
self.path: str = os.path.realpath(path)
def update(self) -> ChangeStatus:
return ChangeStatus(all_changed=True)
def hash(self) -> str:
return "0" * 40
def get_src_dir(self) -> str:
return self.path
def clean(self) -> None:
pass
class SystemPackageFetcher:
def __init__(
self, build_options: BuildOptions, packages: dict[str, list[str]]
) -> None:
self.manager: str | None = build_options.host_type.get_package_manager()
# pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.
self.packages: list[str] | None = packages.get(self.manager)
self.host_type: HostType = build_options.host_type
if self.packages:
self.installed: bool | None = None
else:
self.installed = False
def packages_are_installed(self) -> bool:
if self.installed is not None:
return self.installed
cmd = None
if self.manager == "rpm":
# pyre-fixme[6]: For 1st argument expected
# `pyre_extensions.PyreReadOnly[Iterable[SupportsRichComparisonT]]` but
# got `Optional[List[str]]`.
cmd = ["rpm", "-q"] + sorted(self.packages)
elif self.manager == "deb":
# pyre-fixme[6]: For 1st argument expected
# `pyre_extensions.PyreReadOnly[Iterable[SupportsRichComparisonT]]` but
# got `Optional[List[str]]`.
cmd = ["dpkg", "-s"] + sorted(self.packages)
elif self.manager == "homebrew":
# pyre-fixme[6]: For 1st argument expected
# `pyre_extensions.PyreReadOnly[Iterable[SupportsRichComparisonT]]` but
# got `Optional[List[str]]`.
cmd = ["brew", "ls", "--versions"] + sorted(self.packages)
if cmd:
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if proc.returncode == 0:
# captured as binary as we will hash this later
# pyre-fixme[8]: Attribute has type `Optional[bool]`; used as `bytes`.
self.installed = proc.stdout
else:
# Need all packages to be present to consider us installed
self.installed = False
else:
self.installed = False
return bool(self.installed)
def update(self) -> ChangeStatus:
assert self.installed
return ChangeStatus(all_changed=False)
def hash(self) -> str:
if self.packages_are_installed():
return hashlib.sha256(self.installed).hexdigest()
else:
return "0" * 40
def get_src_dir(self) -> None:
return None
class PreinstalledNopFetcher(SystemPackageFetcher):
def __init__(self) -> None:
self.installed = True
class GitFetcher(Fetcher):
DEFAULT_DEPTH = 1
def __init__(
self,
build_options: BuildOptions,
manifest: ManifestParser,
repo_url: str,
rev: str,
depth: int,
branch: str,
) -> None:
# Extract the host/path portions of the URL and generate a flattened
# directory name. eg:
# github.com/facebook/folly.git -> github.com-facebook-folly.git
url = urlparse(repo_url)
directory = "%s%s%s" % (url.netloc, url.path, branch if branch else "")
for s in ["/", "\\", ":"]:
directory = directory.replace(s, "-")
# Place it in a repos dir in the scratch space
repos_dir = os.path.join(build_options.scratch_dir, "repos")
if not os.path.exists(repos_dir):
os.makedirs(repos_dir)
self.repo_dir: str = os.path.join(repos_dir, directory)
if not rev and build_options.project_hashes:
hash_file = os.path.join(
build_options.project_hashes,
re.sub("\\.git$", "-rev.txt", url.path[1:]),
)
if os.path.exists(hash_file):
with open(hash_file, "r") as f:
data = f.read()
m = re.match("Subproject commit ([a-fA-F0-9]{40})", data)
if not m:
raise Exception("Failed to parse rev from %s" % hash_file)
rev = m.group(1)
print(
"Using pinned rev %s for %s" % (rev, repo_url), file=sys.stderr
)
self.rev: str = rev or branch or "main"
self.origin_repo: str = repo_url
self.manifest: ManifestParser = manifest
self.depth: int = depth if depth else GitFetcher.DEFAULT_DEPTH
self.branch: str = branch
def _update(self) -> ChangeStatus:
current_hash = (
subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=self.repo_dir)
.strip()
.decode("utf-8")
)
target_hash = (
subprocess.check_output(["git", "rev-parse", self.rev], cwd=self.repo_dir)
.strip()
.decode("utf-8")
)
if target_hash == current_hash:
# It's up to date, so there are no changes. This doesn't detect eg:
# if origin/main moved and rev='main', but that's ok for our purposes;
# we should be using explicit hashes or eg: a stable branch for the cases
# that we care about, and it isn't unreasonable to require that the user
# explicitly perform a clean build if those have moved. For the most
# part we prefer that folks build using a release tarball from github
# rather than use the git protocol, as it is generally a bit quicker
# to fetch and easier to hash and verify tarball downloads.
return ChangeStatus()
print("Updating %s -> %s" % (self.repo_dir, self.rev))
run_cmd(["git", "fetch", "origin", self.rev], cwd=self.repo_dir)
run_cmd(["git", "checkout", self.rev], cwd=self.repo_dir)
run_cmd(["git", "submodule", "update", "--init"], cwd=self.repo_dir)
return ChangeStatus(True)
def update(self) -> ChangeStatus:
if os.path.exists(self.repo_dir):
return self._update()
self._clone()
return ChangeStatus(True)
def _clone(self) -> None:
print("Cloning %s..." % self.origin_repo)
# The basename/dirname stuff allows us to dance around issues where
# eg: this python process is native win32, but the git.exe is cygwin
# or msys and doesn't like the absolute windows path that we'd otherwise
# pass to it. Careful use of cwd helps avoid headaches with cygpath.
cmd = [
"git",
"clone",
"--depth=" + str(self.depth),
]
if self.branch:
cmd.append("--branch=" + self.branch)
cmd += [
"--",
self.origin_repo,
os.path.basename(self.repo_dir),
]
run_cmd(cmd, cwd=os.path.dirname(self.repo_dir))
self._update()
def clean(self) -> None:
if os.path.exists(self.repo_dir):
run_cmd(["git", "clean", "-fxd"], cwd=self.repo_dir)
def hash(self) -> str:
return self.rev
def get_src_dir(self) -> str:
return self.repo_dir
def does_file_need_update(
src_name: str, src_st: os.stat_result, dest_name: str
) -> bool:
try:
target_st = os.lstat(dest_name)
except OSError as exc:
if exc.errno != errno.ENOENT:
raise
return True
if src_st.st_size != target_st.st_size:
return True
if stat.S_IFMT(src_st.st_mode) != stat.S_IFMT(target_st.st_mode):
return True
if stat.S_ISLNK(src_st.st_mode):
return os.readlink(src_name) != os.readlink(dest_name)
if not stat.S_ISREG(src_st.st_mode):
return True
# They might have the same content; compare.
with open(src_name, "rb") as sf, open(dest_name, "rb") as df:
chunk_size = 8192
while True:
src_data = sf.read(chunk_size)
dest_data = df.read(chunk_size)
if src_data != dest_data:
return True
if len(src_data) < chunk_size:
# EOF
break
return False
def copy_if_different(src_name: str, dest_name: str) -> bool:
"""Copy src_name -> dest_name, but only touch dest_name
if src_name is different from dest_name, making this a
more build system friendly way to copy."""
src_st = os.lstat(src_name)
if not does_file_need_update(src_name, src_st, dest_name):
return False
dest_parent = os.path.dirname(dest_name)
if not os.path.exists(dest_parent):
os.makedirs(dest_parent)
if stat.S_ISLNK(src_st.st_mode):
try:
os.unlink(dest_name)
except OSError as exc:
if exc.errno != errno.ENOENT:
raise
target = os.readlink(src_name)
os.symlink(target, dest_name)
else:
shutil.copy2(src_name, dest_name)
return True
def filter_strip_marker(dest_name: str, marker: str) -> None:
"""Strip lines/blocks tagged with the given marker from a file."""
try:
with open(dest_name, "r") as f:
content = f.read()
except (UnicodeDecodeError, PermissionError):
return
if marker not in content:
return
escaped = re.escape(marker)
block_re = re.compile(
r"[^\n]*" + escaped + r"-start[^\n]*\n.*?[^\n]*" + escaped + r"-end[^\n]*\n?",
re.DOTALL,
)
line_re = re.compile(r".*" + escaped + r".*\n?")
filtered = block_re.sub("", content)
filtered = line_re.sub("", filtered)
if filtered != content:
with open(dest_name, "w") as f:
f.write(filtered)
def list_files_under_dir_newer_than_timestamp(
dir_to_scan: str, ts: int
) -> Iterator[str]:
for root, _dirs, files in os.walk(dir_to_scan):
for src_file in files:
full_name = os.path.join(root, src_file)
st = os.lstat(full_name)
if st.st_mtime > ts:
yield full_name
class ShipitPathMap:
def __init__(self) -> None:
self.roots: list[str] = []
self.mapping: list[str] = []
self.exclusion: list[str] = []
self.strip_marker: str = "@fb-only"
def add_mapping(self, fbsource_dir: str, target_dir: str) -> None:
"""Add a posix path or pattern. We cannot normpath the input
here because that would change the paths from posix to windows
form and break the logic throughout this class."""
self.roots.append(fbsource_dir)
# pyre-fixme[6]: For 1st argument expected `str` but got `Tuple[str, str]`.
self.mapping.append((fbsource_dir, target_dir))
def add_exclusion(self, pattern: str) -> None:
# pyre-fixme[6]: For 1st argument expected `str` but got `Pattern[str]`.
self.exclusion.append(re.compile(pattern))
def _minimize_roots(self) -> None:
"""compute the de-duplicated set of roots within fbsource.
We take the shortest common directory prefix to make this
determination"""
self.roots.sort(key=len)
minimized = []
for r in self.roots:
add_this_entry = True
for existing in minimized:
if r.startswith(existing + "/"):
add_this_entry = False
break
if add_this_entry:
minimized.append(r)
self.roots = minimized
def _sort_mapping(self) -> None:
self.mapping.sort(reverse=True, key=lambda x: len(x[0]))
def _map_name(self, norm_name: str, dest_root: str) -> str | None:
if norm_name.endswith(".pyc") or norm_name.endswith(".swp"):
# Ignore some incidental garbage while iterating
return None
for excl in self.exclusion:
# pyre-fixme[16]: `str` has no attribute `match`.
if excl.match(norm_name):
return None
for src_name, dest_name in self.mapping:
if norm_name == src_name or norm_name.startswith(src_name + "/"):
rel_name = os.path.relpath(norm_name, src_name)
# We can have "." as a component of some paths, depending
# on the contents of the shipit transformation section.
# normpath doesn't always remove `.` as the final component
# of the path, which be problematic when we later mkdir
# the dirname of the path that we return. Take care to avoid
# returning a path with a `.` in it.
rel_name = os.path.normpath(rel_name)
if dest_name == ".":
return os.path.normpath(os.path.join(dest_root, rel_name))
dest_name = os.path.normpath(dest_name)
return os.path.normpath(os.path.join(dest_root, dest_name, rel_name))
raise Exception("%s did not match any rules" % norm_name)
def mirror(self, fbsource_root: str, dest_root: str) -> ChangeStatus:
self._minimize_roots()
self._sort_mapping()
change_status = ChangeStatus()
# Record the full set of files that should be in the tree
full_file_list = set()
if sys.platform == "win32":
# Let's not assume st_dev has a consistent value on Windows.
def st_dev(path: str) -> int:
return 1
else:
def st_dev(path: str) -> int:
return os.lstat(path).st_dev
for fbsource_subdir in self.roots:
dir_to_mirror = os.path.join(fbsource_root, fbsource_subdir)
root_dev = st_dev(dir_to_mirror)
prefetch_dir_if_eden(dir_to_mirror)
if not os.path.exists(dir_to_mirror):
raise Exception(
"%s doesn't exist; check your sparse profile!" % dir_to_mirror
)
update_count = 0
for root, dirs, files in os.walk(dir_to_mirror):
dirs[:] = [d for d in dirs if root_dev == st_dev(os.path.join(root, d))]
for src_file in files:
full_name = os.path.join(root, src_file)
rel_name = os.path.relpath(full_name, fbsource_root)
norm_name = rel_name.replace("\\", "/")
target_name = self._map_name(norm_name, dest_root)
if target_name:
full_file_list.add(target_name)
if copy_if_different(full_name, target_name):
filter_strip_marker(target_name, self.strip_marker)
change_status.record_change(target_name)
if update_count < 10:
print("Updated %s -> %s" % (full_name, target_name))
elif update_count == 10:
print("...")
update_count += 1
if update_count:
print("Updated %s for %s" % (update_count, fbsource_subdir))
# Compare the list of previously shipped files; if a file is
# in the old list but not the new list then it has been
# removed from the source and should be removed from the
# destination.
# Why don't we simply create this list by walking dest_root?
# Some builds currently have to be in-source builds and
# may legitimately need to keep some state in the source tree :-/
installed_name = os.path.join(dest_root, ".shipit_shipped")
if os.path.exists(installed_name):
with open(installed_name, "rb") as f:
for name in f.read().decode("utf-8").splitlines():
name = name.strip()
if name not in full_file_list:
print("Remove %s" % name)
os.unlink(name)
change_status.record_change(name)
with open(installed_name, "wb") as f:
for name in sorted(full_file_list):
f.write(("%s\n" % name).encode("utf-8"))
return change_status
class FbsourceRepoData(NamedTuple):
hash: str
date: str
FBSOURCE_REPO_DATA: dict[str, FbsourceRepoData] = {}
def get_fbsource_repo_data(build_options: BuildOptions) -> FbsourceRepoData:
"""Returns the commit metadata for the fbsource repo.
Since we may have multiple first party projects to
hash, and because we don't mutate the repo, we cache
this hash in a global."""
# pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.
cached_data = FBSOURCE_REPO_DATA.get(build_options.fbsource_dir)
if cached_data:
return cached_data
if "GETDEPS_HG_REPO_DATA" in os.environ:
log_data = os.environ["GETDEPS_HG_REPO_DATA"]
else:
cmd = ["hg", "log", "-r.", "-T{node}\n{date|hgdate}"]
env = Env()
env.set("HGPLAIN", "1")
log_data = subprocess.check_output(
cmd, cwd=build_options.fbsource_dir, env=dict(env.items())
).decode("ascii")
(hash, datestr) = log_data.split("\n")
# datestr is like "seconds fractionalseconds"
# We want "20200324.113140"
(unixtime, _fractional) = datestr.split(" ")
date = datetime.fromtimestamp(int(unixtime)).strftime("%Y%m%d.%H%M%S")
cached_data = FbsourceRepoData(hash=hash, date=date)
# pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.
FBSOURCE_REPO_DATA[build_options.fbsource_dir] = cached_data
return cached_data
def is_public_commit(build_options: BuildOptions) -> bool: # noqa: C901
"""Check if the current commit is public (shipped/will be shipped to remote).
Works across git, sapling (sl), and hg repositories:
- For hg/sapling: Uses 'phase' command to check if commit is public
- For git: Checks if commit exists in remote branches
Returns True if public, False if draft/local-only or on error (conservative).
"""
# Use fbsource_dir if available (Meta internal), otherwise fall back to repo_root
repo_dir = build_options.fbsource_dir or build_options.repo_root
if not repo_dir:
# No repository detected, be conservative
return False
env = Env()
env.set("HGPLAIN", "1")
env_dict = dict(env.items())
try:
# Try hg/sapling phase command first (works for both hg and sl)
# Try 'sl' first as it's the preferred tool at Meta
for cmd in [["sl", "phase", "-r", "."], ["hg", "phase", "-r", "."]]:
try:
output = (
subprocess.check_output(
cmd, cwd=repo_dir, env=env_dict, stderr=subprocess.DEVNULL
)
.decode("ascii")
.strip()
)
# Output format: "hash: public" or "hash: draft"
return "public" in output
except (subprocess.CalledProcessError, FileNotFoundError):
continue
# Try git if hg/sl didn't work
try:
# Detect the default branch for origin remote
default_branch = None
try:
# Get the symbolic ref for origin/HEAD to find default branch
output = (
subprocess.check_output(
["git", "symbolic-ref", "refs/remotes/origin/HEAD"],
cwd=repo_dir,
stderr=subprocess.DEVNULL,
)
.decode("ascii")
.strip()
)
# Output format: "refs/remotes/origin/main"
if output.startswith("refs/remotes/"):
default_branch = output
except subprocess.CalledProcessError:
# If symbolic-ref fails, fall back to common names
pass
# Build list of branches to check
branches_to_check = []
if default_branch:
branches_to_check.append(default_branch)
# Also try common defaults as fallback
branches_to_check.extend(["origin/main", "origin/master"])
# Check if HEAD is an ancestor of any of these branches
for branch in branches_to_check:
try:
subprocess.check_output(
["git", "merge-base", "--is-ancestor", "HEAD", branch],
cwd=repo_dir,
stderr=subprocess.DEVNULL,
)
# If command succeeds (exit 0), HEAD is an ancestor of the branch
return True
except subprocess.CalledProcessError:
# Not an ancestor of this branch, try next
continue
# HEAD is not in any default branch
return False
except FileNotFoundError:
pass
# If all VCS commands failed, be conservative and don't upload
return False
except Exception:
# On any unexpected error, be conservative and don't upload
return False
class SimpleShipitTransformerFetcher(Fetcher):
def __init__(
self,
build_options: BuildOptions,
manifest: ManifestParser,
ctx: ManifestContext,
) -> None:
self.build_options: BuildOptions = build_options
self.manifest: ManifestParser = manifest
self.repo_dir: str = os.path.join(
build_options.scratch_dir, "shipit", manifest.name
)
self.ctx: ManifestContext = ctx
def clean(self) -> None:
if os.path.exists(self.repo_dir):
shutil.rmtree(self.repo_dir)
def update(self) -> ChangeStatus:
mapping = ShipitPathMap()
for src, dest in self.manifest.get_section_as_ordered_pairs(
"shipit.pathmap", self.ctx
):
# pyre-fixme[6]: For 2nd argument expected `str` but got `Optional[str]`.
mapping.add_mapping(src, dest)
if self.manifest.shipit_fbcode_builder:
mapping.add_mapping(
"fbcode/opensource/fbcode_builder", "build/fbcode_builder"
)
for pattern in self.manifest.get_section_as_args("shipit.strip", self.ctx):
mapping.add_exclusion(pattern)
# pyre-fixme[8]: Attribute has type `str`; used as `Optional[str]`.
mapping.strip_marker = self.manifest.shipit_strip_marker
# pyre-fixme[6]: In call `ShipitPathMap.mirror`, for 1st positional argument, expected `str` but got `Optional[str]`
return mapping.mirror(self.build_options.fbsource_dir, self.repo_dir)
def hash(self) -> str:
# We return a fixed non-hash string for in-fbsource builds.
# We're relying on the `update` logic to correctly invalidate
# the build in the case that files have changed.
return "fbsource"
def get_src_dir(self) -> str:
return self.repo_dir
class SubFetcher(Fetcher):
"""Fetcher for a project with subprojects"""
def __init__(self, base: Fetcher, subs: list[tuple[Fetcher, str]]) -> None:
self.base: Fetcher = base
self.subs: list[tuple[Fetcher, str]] = subs
def update(self) -> ChangeStatus:
base = self.base.update()
changed = base.build_changed() or base.sources_changed()
for fetcher, dir in self.subs:
stat = fetcher.update()
if stat.build_changed() or stat.sources_changed():
changed = True
link = self.base.get_src_dir() + "/" + dir
if not os.path.exists(link):
os.symlink(fetcher.get_src_dir(), link)
return ChangeStatus(changed)
def clean(self) -> None:
self.base.clean()
for fetcher, _ in self.subs:
fetcher.clean()
def hash(self) -> str:
my_hash = self.base.hash()
for fetcher, _ in self.subs:
my_hash += fetcher.hash()
return my_hash
def get_src_dir(self) -> str:
return self.base.get_src_dir()
class ShipitTransformerFetcher(Fetcher):
@classmethod
def _shipit_paths(cls, build_options: BuildOptions) -> list[str]:
www_path = ["/var/www/scripts/opensource/codesync"]
if build_options.fbsource_dir:
fbcode_path = [
os.path.join(
build_options.fbsource_dir,
"fbcode/opensource/codesync/codesync-cli/codesync",
)
]
else:
fbcode_path = []
return www_path + fbcode_path
def __init__(
self, build_options: BuildOptions, project_name: str, external_branch: str
) -> None:
self.build_options: BuildOptions = build_options
self.project_name: str = project_name
self.external_branch: str = external_branch
self.repo_dir: str = os.path.join(
build_options.scratch_dir, "shipit", project_name
)
self.shipit: str | None = None
for path in ShipitTransformerFetcher._shipit_paths(build_options):
if os.path.exists(path):
self.shipit = path
break
def update(self) -> ChangeStatus:
if os.path.exists(self.repo_dir):
return ChangeStatus()
self.run_shipit()
return ChangeStatus(True)
def clean(self) -> None:
if os.path.exists(self.repo_dir):
shutil.rmtree(self.repo_dir)
@classmethod
def available(cls, build_options: BuildOptions) -> bool:
return any(
os.path.exists(path)
for path in ShipitTransformerFetcher._shipit_paths(build_options)
)
def run_shipit(self) -> None:
tmp_path = self.repo_dir + ".new"
try:
if os.path.exists(tmp_path):
shutil.rmtree(tmp_path)
os.makedirs(os.path.dirname(tmp_path), exist_ok=True)
cmd = [
self.shipit,
"shipit",
"--project=" + self.project_name,
"--create-new-repo",
# pyre-fixme[58]: `+` is not supported for operand types `str` and
# `Optional[str]`.
"--source-repo-dir=" + self.build_options.fbsource_dir,
"--source-branch=.",
"--skip-source-init",
"--skip-source-pull",
"--skip-source-clean",
"--skip-push",
"--destination-use-anonymous-https",
"--create-new-repo-output-path=" + tmp_path,
]
if self.external_branch:
cmd += [
f"--external-branch={self.external_branch}",
]
# Run shipit
# pyre-fixme[6]: For 1st argument expected `List[str]` but got
# `List[Optional[str]]`.
run_cmd(cmd)
# Remove the .git directory from the repository it generated.
# There is no need to commit this.
repo_git_dir = os.path.join(tmp_path, ".git")
shutil.rmtree(repo_git_dir)
os.rename(tmp_path, self.repo_dir)
except Exception:
# Clean up after a failed extraction
if os.path.exists(tmp_path):
shutil.rmtree(tmp_path)
self.clean()
raise
def hash(self) -> str:
# We return a fixed non-hash string for in-fbsource builds.
return "fbsource"
def get_src_dir(self) -> str:
return self.repo_dir
def download_url_to_file_with_progress(url: str, file_name: str) -> None:
print("Download with %s -> %s ..." % (url, file_name))
class Progress:
last_report: float = 0
def write_update(self, total: int, amount: int) -> None:
if total == -1:
total = "(Unknown)"
if sys.stdout.isatty():
sys.stdout.write("\r downloading %s of %s " % (amount, total))
else:
# When logging to CI logs, avoid spamming the logs and print
# status every few seconds
now = time.time()
if now - self.last_report > 5:
sys.stdout.write(".. %s of %s " % (amount, total))
self.last_report = now
sys.stdout.flush()
def progress_pycurl(
self, total: float, amount: float, _uploadtotal: float, _uploadamount: float
) -> None:
self.write_update(total, amount)
progress = Progress()
start = time.time()
try:
if os.environ.get("GETDEPS_USE_WGET") is not None:
procargs = (
[
"wget",
]
+ os.environ.get("GETDEPS_WGET_ARGS", "").split()
+ [
"-O",
file_name,
url,
]
)
subprocess.run(procargs, capture_output=True)
headers = None
elif os.environ.get("GETDEPS_USE_LIBCURL") is not None:
import pycurl
with open(file_name, "wb") as f:
c = pycurl.Curl()
c.setopt(pycurl.URL, url)
c.setopt(pycurl.WRITEDATA, f)
# display progress
c.setopt(pycurl.NOPROGRESS, False)
c.setopt(pycurl.XFERINFOFUNCTION, progress.progress_pycurl)
c.perform()
c.close()
headers = None
else:
try:
req_header = {"Accept": "application/*"}
res = urlopen(Request(url, None, req_header))
chunk_size = 8192 # urlretrieve uses this value
headers = res.headers
content_length = res.headers.get("Content-Length")
total = int(content_length.strip()) if content_length else -1
amount = 0
with open(file_name, "wb") as f:
chunk = res.read(chunk_size)
while chunk:
f.write(chunk)
amount += len(chunk)
progress.write_update(total, amount)
chunk = res.read(chunk_size)
except (OSError, IOError) as exc: # noqa: B014
# Downloading from within Meta's network needs to use a proxy.
if shutil.which("fwdproxy-config") is None:
print(
"Note: Could not find Meta-specific fallback 'fwdproxy-config'. "
"If you are working externally, you can ignore this message."
)
raise
print("Default download failed, retrying with curl and fwdproxy...")
cmd = f"curl -L $(fwdproxy-config curl) -o {shlex.quote(file_name)} {shlex.quote(url)}"
print(f"Running command: {cmd}")
result = subprocess.run(cmd, shell=True, capture_output=True)
if result.returncode != 0:
raise TransientFailure(
f"Failed to download {url} to {file_name}: {exc} (fwdproxy fallback failed: {result.stderr.decode()})"
)
headers = None
except (OSError, IOError) as exc: # noqa: B014
raise TransientFailure(
"Failed to download %s to %s: %s" % (url, file_name, str(exc))
)
end = time.time()
sys.stdout.write(" [Complete in %f seconds]\n" % (end - start))
sys.stdout.flush()
if headers is not None:
print(f"{headers}")
class ArchiveFetcher(Fetcher):
def __init__(
self,
build_options: BuildOptions,
manifest: ManifestParser,
url: str,
sha256: str,
) -> None:
self.manifest: ManifestParser = manifest
self.url: str = url
self.sha256: str = sha256
self.build_options: BuildOptions = build_options
parsed_url = urlparse(self.url)
basename = "%s-%s" % (manifest.name, os.path.basename(parsed_url.path))
self.file_name: str = os.path.join(
build_options.scratch_dir, "downloads", basename
)
self.src_dir: str = os.path.join(
build_options.scratch_dir, "extracted", basename
)
self.hash_file: str = self.src_dir + ".hash"
def _verify_hash(self) -> None:
h = hashlib.sha256()
with open(self.file_name, "rb") as f:
while True:
block = f.read(8192)
if not block:
break
h.update(block)
digest = h.hexdigest()
if digest != self.sha256:
os.unlink(self.file_name)
raise Exception(
"%s: expected sha256 %s but got %s" % (self.url, self.sha256, digest)
)
def _download_dir(self) -> str:
"""returns the download dir, creating it if it doesn't already exist"""
download_dir = os.path.dirname(self.file_name)
if not os.path.exists(download_dir):
os.makedirs(download_dir)
return download_dir
def _download(self) -> None:
self._download_dir()
max_attempts = 5
delay = 1
for attempt in range(max_attempts):
try:
download_url_to_file_with_progress(self.url, self.file_name)
break
except TransientFailure as tf:
if attempt < max_attempts - 1:
delay *= 2
delay_with_jitter = delay * (1 + random.random() * 0.1)
time.sleep(min(delay_with_jitter, 10))
else:
print(f"Failed after retries: {tf}")
raise
self._verify_hash()
def clean(self) -> None:
if os.path.exists(self.src_dir):
shutil.rmtree(self.src_dir)
def update(self) -> ChangeStatus:
try:
with open(self.hash_file, "r") as f:
saved_hash = f.read().strip()
if saved_hash == self.sha256 and os.path.exists(self.src_dir):
# Everything is up to date
return ChangeStatus()
print(
"saved hash %s doesn't match expected hash %s, re-validating"
% (saved_hash, self.sha256)
)
os.unlink(self.hash_file)
except EnvironmentError:
pass
# If we got here we know the contents of src_dir are either missing
# or wrong, so blow away whatever happened to be there first.
if os.path.exists(self.src_dir):
shutil.rmtree(self.src_dir)
# If we already have a file here, make sure it looks legit before
# proceeding: any errors and we just remove it and re-download
if os.path.exists(self.file_name):
try:
self._verify_hash()
except Exception:
if os.path.exists(self.file_name):
os.unlink(self.file_name)
if not os.path.exists(self.file_name):
self._download()
self._verify_hash()
if tarfile.is_tarfile(self.file_name):
opener = tarfile.open
elif zipfile.is_zipfile(self.file_name):
opener = zipfile.ZipFile
else:
raise Exception("don't know how to extract %s" % self.file_name)
os.makedirs(self.src_dir)
print("Extract %s -> %s" % (self.file_name, self.src_dir))
if is_windows():
# Ensure that we don't fall over when dealing with long paths
# on windows
src = r"\\?\%s" % os.path.normpath(self.src_dir)
else:
src = self.src_dir
with opener(self.file_name) as t:
# The `str` here is necessary to ensure that we don't pass a unicode
# object down to tarfile.extractall on python2. When extracting
# the boost tarball it makes some assumptions and tries to convert
# a non-ascii path to ascii and throws.
src = str(src)
t.extractall(src)
if is_windows():
subdir = self.manifest.get("build", "subdir")
checkdir = src
if subdir:
checkdir = src + "\\" + subdir
if os.path.exists(checkdir):
children = os.listdir(checkdir)
print(f"Extracted to {checkdir} contents: {children}")
with open(self.hash_file, "w") as f:
f.write(self.sha256)
return ChangeStatus(True)
def hash(self) -> str:
return self.sha256
def get_src_dir(self) -> str:
return self.src_dir
def homebrew_package_prefix(package: str) -> str | None:
cmd = ["brew", "--prefix", package]
try:
proc = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except FileNotFoundError:
return None
if proc.returncode == 0:
return proc.stdout.decode("utf-8").rstrip()
================================================
FILE: build/fbcode_builder/getdeps/include_rewriter.py
================================================
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
"""
Include Path Rewriter for getdeps
This module provides functionality to rewrite #include statements in C++ files
to handle differences between fbcode and open source project structures.
"""
import os
import re
import typing
from pathlib import Path
from typing import Any
if typing.TYPE_CHECKING:
from .manifest import ManifestParser
class IncludePathRewriter:
"""Rewrites #include paths in C++ source files based on path mappings."""
# C++ file extensions to process
CPP_EXTENSIONS: set[str] = {
".cpp",
".cc",
".cxx",
".c",
".h",
".hpp",
".hxx",
".tcc",
".inc",
}
def __init__(self, mappings: list[tuple[str, str]], verbose: bool = False) -> None:
"""
Initialize the rewriter with path mappings.
Args:
mappings: List of (old_path_prefix, new_path_prefix) tuples
verbose: Enable verbose output
"""
self.mappings: list[tuple[str, str]] = mappings
self.verbose: bool = verbose
# Compile regex patterns for efficiency
self.patterns: list[tuple[re.Pattern[str], str, str]] = []
for old_prefix, new_prefix in mappings:
# Match both quoted and angle bracket includes
# Pattern matches: #include "old_prefix/rest" or #include
pattern = re.compile(
r'(#\s*include\s*[<"])(' + re.escape(old_prefix) + r'/[^">]+)([">])',
re.MULTILINE,
)
self.patterns.append((pattern, old_prefix, new_prefix))
def rewrite_file(self, file_path: Path, dry_run: bool = False) -> bool:
"""
Rewrite includes in a single file.
Args:
file_path: Path to the file to process
dry_run: If True, don't actually modify files
Returns:
True if file was modified, False otherwise
"""
try:
with open(file_path, "r", encoding="utf-8") as f:
original_content: str = f.read()
except (IOError, UnicodeDecodeError) as e:
if self.verbose:
print(f"Warning: Could not read {file_path}: {e}")
return False
modified_content: str = original_content
changes_made: bool = False
for pattern, old_prefix, new_prefix in self.patterns:
def make_replace_func(
old_prefix: str, new_prefix: str
) -> typing.Callable[[re.Match[str]], str]:
def replace_func(match: re.Match[str]) -> str:
nonlocal changes_made
prefix: str = match.group(1) # #include [<"]
full_path: str = match.group(2) # full path
suffix: str = match.group(3) # [">]
# Replace the old prefix with new prefix
new_path: str = full_path.replace(old_prefix, new_prefix, 1)
if self.verbose and not changes_made:
print(f" {full_path} -> {new_path}")
changes_made = True
return f"{prefix}{new_path}{suffix}"
return replace_func
modified_content = pattern.sub(
make_replace_func(old_prefix, new_prefix), modified_content
)
if changes_made and not dry_run:
try:
with open(file_path, "w", encoding="utf-8") as f:
f.write(modified_content)
if self.verbose:
print(f"Modified: {file_path}")
except IOError as e:
print(f"Error: Could not write {file_path}: {e}")
return False
elif changes_made and dry_run:
if self.verbose:
print(f"Would modify: {file_path}")
return changes_made
def process_directory(self, source_dir: Path, dry_run: bool = False) -> int:
"""
Process all C++ files in a directory recursively.
Args:
source_dir: Root directory to process
dry_run: If True, don't actually modify files
Returns:
Number of files modified
"""
if not source_dir.exists():
if self.verbose:
print(f"Warning: Directory {source_dir} does not exist")
return 0
modified_count: int = 0
processed_count: int = 0
for root, dirs, files in os.walk(source_dir):
# Skip hidden directories and common build directories
dirs[:] = [
d
for d in dirs
if not d.startswith(".")
and d not in {"build", "_build", "__pycache__", "CMakeFiles"}
]
for file in files:
file_path: Path = Path(root) / file
# Only process C++ files
if file_path.suffix.lower() not in self.CPP_EXTENSIONS:
continue
processed_count += 1
if self.verbose:
print(f"Processing: {file_path}")
if self.rewrite_file(file_path, dry_run):
modified_count += 1
if self.verbose or modified_count > 0:
print(f"Processed {processed_count} files, modified {modified_count} files")
return modified_count
def rewrite_includes_from_manifest(
manifest: ManifestParser, ctx: Any, source_dir: str, verbose: bool = False
) -> int:
"""
Rewrite includes using mappings from a manifest file.
Args:
manifest: The manifest object containing shipit.pathmap section
ctx: The manifest context
source_dir: Directory containing source files to process
verbose: Enable verbose output
Returns:
Number of files modified
"""
mappings: list[tuple[str, str]] = []
# Get mappings from the manifest's shipit.pathmap section
for src, dest in manifest.get_section_as_ordered_pairs("shipit.pathmap", ctx):
# Remove fbcode/ or xplat/ prefixes from src since they won't appear in #include statements
if src.startswith("fbcode/"):
src = src[len("fbcode/") :]
elif src.startswith("xplat/"):
src = src[len("xplat/") :]
# pyre-fixme[6]: For 1st argument expected `Tuple[str, str]` but got
# `Tuple[str, Optional[str]]`.
mappings.append((src, dest))
if not mappings:
if verbose:
print("No include path mappings found in manifest")
return 0
if verbose:
print("Include path mappings:")
for old_path, new_path in mappings:
print(f" {old_path} -> {new_path}")
rewriter: IncludePathRewriter = IncludePathRewriter(mappings, verbose)
return rewriter.process_directory(Path(source_dir), dry_run=False)
================================================
FILE: build/fbcode_builder/getdeps/load.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import base64
import copy
import hashlib
import os
import typing
from collections.abc import Iterator
from . import fetcher
from .envfuncs import path_search
from .errors import ManifestNotFound
from .manifest import ManifestParser
if typing.TYPE_CHECKING:
from .buildopts import BuildOptions
from .manifest import ContextGenerator, ManifestContext
class Loader:
"""The loader allows our tests to patch the load operation"""
def _list_manifests(self, build_opts: BuildOptions) -> Iterator[str]:
"""Returns a generator that iterates all the available manifests"""
for path, _, files in os.walk(build_opts.manifests_dir):
for name in files:
# skip hidden files
if name.startswith("."):
continue
yield os.path.join(path, name)
def _load_manifest(self, path: str) -> ManifestParser:
return ManifestParser(path)
def load_project(
self, build_opts: BuildOptions, project_name: str
) -> ManifestParser:
if "/" in project_name or "\\" in project_name:
# Assume this is a path already
return ManifestParser(project_name)
for manifest in self._list_manifests(build_opts):
if os.path.basename(manifest) == project_name:
return ManifestParser(manifest)
raise ManifestNotFound(project_name)
def load_all(self, build_opts: BuildOptions) -> dict[str, ManifestParser]:
manifests_by_name: dict[str, ManifestParser] = {}
for manifest in self._list_manifests(build_opts):
m = self._load_manifest(manifest)
if m.name in manifests_by_name:
raise Exception("found duplicate manifest '%s'" % m.name)
manifests_by_name[m.name] = m
return manifests_by_name
class ResourceLoader(Loader):
def __init__(self, namespace: str, manifests_dir: str) -> None:
self.namespace: str = namespace
self.manifests_dir: str = manifests_dir
def _list_manifests(self, build_opts: BuildOptions) -> Iterator[str]:
import pkg_resources
dirs: list[str] = [self.manifests_dir]
while dirs:
current = dirs.pop(0)
for name in pkg_resources.resource_listdir(self.namespace, current):
path = "%s/%s" % (current, name)
if pkg_resources.resource_isdir(self.namespace, path):
dirs.append(path)
else:
yield "%s/%s" % (current, name)
def _find_manifest(self, project_name: str) -> str:
# pyre-fixme[20]: Call `ResourceLoader._list_manifests` expects argument `build_opts`.
for name in self._list_manifests():
if name.endswith("/%s" % project_name):
return name
raise ManifestNotFound(project_name)
def _load_manifest(self, path: str) -> ManifestParser:
import pkg_resources
contents = pkg_resources.resource_string(self.namespace, path).decode("utf8")
return ManifestParser(file_name=path, fp=contents)
def load_project(
self, build_opts: BuildOptions, project_name: str
) -> ManifestParser:
project_name = self._find_manifest(project_name)
# pyre-fixme[16]: `ResourceLoader` has no attribute `_load_resource_manifest`.
return self._load_resource_manifest(project_name)
LOADER: Loader = Loader()
def patch_loader(namespace: str, manifests_dir: str = "manifests") -> None:
global LOADER
LOADER = ResourceLoader(namespace, manifests_dir)
def load_project(build_opts: BuildOptions, project_name: str) -> ManifestParser:
"""given the name of a project or a path to a manifest file,
load up the ManifestParser instance for it and return it"""
return LOADER.load_project(build_opts, project_name)
def load_all_manifests(build_opts: BuildOptions) -> dict[str, ManifestParser]:
return LOADER.load_all(build_opts)
class ManifestLoader:
"""ManifestLoader stores information about project manifest relationships for a
given set of (build options + platform) configuration.
The ManifestLoader class primarily serves as a location to cache project dependency
relationships and project hash values for this build configuration.
"""
def __init__(
self, build_opts: BuildOptions, ctx_gen: ContextGenerator | None = None
) -> None:
self._loader: Loader = LOADER
self.build_opts: BuildOptions = build_opts
if ctx_gen is None:
self.ctx_gen: ContextGenerator = self.build_opts.get_context_generator()
else:
self.ctx_gen = ctx_gen
self.manifests_by_name: dict[str, ManifestParser] = {}
self._loaded_all: bool = False
self._project_hashes: dict[str, str] = {}
self._fetcher_overrides: dict[str, fetcher.LocalDirFetcher] = {}
self._build_dir_overrides: dict[str, str] = {}
self._install_dir_overrides: dict[str, str] = {}
self._install_prefix_overrides: dict[str, str] = {}
def load_manifest(self, name: str) -> ManifestParser:
manifest = self.manifests_by_name.get(name)
if manifest is None:
manifest = self._loader.load_project(self.build_opts, name)
self.manifests_by_name[name] = manifest
return manifest
def load_all_manifests(self) -> dict[str, ManifestParser]:
if not self._loaded_all:
all_manifests_by_name = self._loader.load_all(self.build_opts)
if self.manifests_by_name:
# To help ensure that we only ever have a single manifest object for a
# given project, and that it can't change once we have loaded it,
# only update our mapping for projects that weren't already loaded.
for name, manifest in all_manifests_by_name.items():
self.manifests_by_name.setdefault(name, manifest)
else:
self.manifests_by_name = all_manifests_by_name
self._loaded_all = True
return self.manifests_by_name
def dependencies_of(self, manifest: ManifestParser) -> list[ManifestParser]:
"""Returns the dependencies of the given project, not including the project itself, in topological order."""
return [
dep
for dep in self.manifests_in_dependency_order(manifest)
if dep != manifest
]
def manifests_in_dependency_order(
self, manifest: ManifestParser | None = None
) -> list[ManifestParser]:
"""Compute all dependencies of the specified project. Returns a list of the
dependencies plus the project itself, in topologically sorted order.
Each entry in the returned list only depends on projects that appear before it
in the list.
If the input manifest is None, the dependencies for all currently loaded
projects will be computed. i.e., if you call load_all_manifests() followed by
manifests_in_dependency_order() this will return a global dependency ordering of
all projects."""
# The list of deps that have been fully processed
seen: set[str] = set()
# The list of deps which have yet to be evaluated. This
# can potentially contain duplicates.
if manifest is None:
deps: list[ManifestParser] = list(self.manifests_by_name.values())
else:
assert manifest.name in self.manifests_by_name
deps = [manifest]
# The list of manifests in dependency order
dep_order: list[ManifestParser] = []
system_packages: dict[str, list[str]] = {}
while len(deps) > 0:
m = deps.pop(0)
if m.name in seen:
continue
# Consider its deps, if any.
# We sort them for increased determinism; we'll produce
# a correct order even if they aren't sorted, but we prefer
# to produce the same order regardless of how they are listed
# in the project manifest files.
ctx: ManifestContext = self.ctx_gen.get_context(m.name)
dep_list: list[str] = m.get_dependencies(ctx)
dep_count: int = 0
for dep_name in dep_list:
# If we're not sure whether it is done, queue it up
if dep_name not in seen:
dep = self.manifests_by_name.get(dep_name)
if dep is None:
dep = self._loader.load_project(self.build_opts, dep_name)
self.manifests_by_name[dep.name] = dep
deps.append(dep)
dep_count += 1
if dep_count > 0:
# If we queued anything, re-queue this item, as it depends
# those new item(s) and their transitive deps.
deps.append(m)
continue
# Its deps are done, so we can emit it
seen.add(m.name)
# Capture system packages as we may need to set PATHs to then later
if (
self.build_opts.allow_system_packages
and self.build_opts.host_type.get_package_manager()
):
packages: dict[str, list[str]] = m.get_required_system_packages(ctx)
for pkg_type, v in packages.items():
merged: list[str] = system_packages.get(pkg_type, [])
if v not in merged:
merged += v
system_packages[pkg_type] = merged
# A manifest depends on all system packages in it dependencies as well
# pyre-fixme[8]: Attribute has type `Dict[str, str]`; used as
# `Dict[str, List[str]]`.
m.resolved_system_packages = copy.copy(system_packages)
dep_order.append(m)
return dep_order
def set_project_src_dir(self, project_name: str, path: str) -> None:
self._fetcher_overrides[project_name] = fetcher.LocalDirFetcher(path)
def set_project_build_dir(self, project_name: str, path: str) -> None:
self._build_dir_overrides[project_name] = path
def set_project_install_dir(self, project_name: str, path: str) -> None:
self._install_dir_overrides[project_name] = path
def set_project_install_prefix(self, project_name: str, path: str) -> None:
self._install_prefix_overrides[project_name] = path
def create_fetcher(
self, manifest: ManifestParser
) -> fetcher.Fetcher | fetcher.LocalDirFetcher:
override = self._fetcher_overrides.get(manifest.name)
if override is not None:
return override
ctx: ManifestContext = self.ctx_gen.get_context(manifest.name)
return manifest.create_fetcher(self.build_opts, self, ctx)
def get_project_hash(self, manifest: ManifestParser) -> str:
h = self._project_hashes.get(manifest.name)
if h is None:
h = self._compute_project_hash(manifest)
self._project_hashes[manifest.name] = h
return h
def _compute_project_hash(self, manifest: ManifestParser) -> str:
"""This recursive function computes a hash for a given manifest.
The hash takes into account some environmental factors on the
host machine and includes the hashes of its dependencies.
No caching of the computation is performed, which is theoretically
wasteful but the computation is fast enough that it is not required
to cache across multiple invocations."""
ctx: ManifestContext = self.ctx_gen.get_context(manifest.name)
hasher = hashlib.sha256()
# Some environmental and configuration things matter
env: dict[str, str | None] = {}
env["install_dir"] = self.build_opts.install_dir
env["scratch_dir"] = self.build_opts.scratch_dir
env["vcvars_path"] = self.build_opts.vcvars_path
env["os"] = self.build_opts.host_type.ostype
env["distro"] = self.build_opts.host_type.distro
env["distro_vers"] = self.build_opts.host_type.distrovers
env["shared_libs"] = str(self.build_opts.shared_libs)
for name in [
"CXXFLAGS",
"CPPFLAGS",
"LDFLAGS",
"CXX",
"CC",
"GETDEPS_CMAKE_DEFINES",
]:
env[name] = os.environ.get(name)
for tool in ["cc", "c++", "gcc", "g++", "clang", "clang++"]:
env["tool-%s" % tool] = path_search(os.environ, tool)
for name in manifest.get_section_as_args("depends.environment", ctx):
env[name] = os.environ.get(name)
fetcher_inst: fetcher.Fetcher | fetcher.LocalDirFetcher = self.create_fetcher(
manifest
)
env["fetcher.hash"] = fetcher_inst.hash()
for name in sorted(env.keys()):
hasher.update(name.encode("utf-8"))
value = env.get(name)
if value is not None:
try:
hasher.update(value.encode("utf-8"))
except AttributeError as exc:
raise AttributeError("name=%r, value=%r: %s" % (name, value, exc))
manifest.update_hash(hasher, ctx)
# If a patchfile is specified, include its contents in the hash
patchfile: str | None = manifest.get("build", "patchfile", ctx=ctx)
if patchfile:
patchfile_path: str = os.path.join(
self.build_opts.fbcode_builder_dir, "patches", patchfile
)
if os.path.exists(patchfile_path):
with open(patchfile_path, "rb") as f:
hasher.update(f.read())
dep_list: list[str] = manifest.get_dependencies(ctx)
for dep in dep_list:
dep_manifest: ManifestParser = self.load_manifest(dep)
dep_hash: str = self.get_project_hash(dep_manifest)
hasher.update(dep_hash.encode("utf-8"))
# Use base64 to represent the hash, rather than the simple hex digest,
# so that the string is shorter. Use the URL-safe encoding so that
# the hash can also be safely used as a filename component.
h: str = base64.urlsafe_b64encode(hasher.digest()).decode("ascii")
# ... and because cmd.exe is troublesome with `=` signs, nerf those.
# They tend to be padding characters at the end anyway, so we can
# safely discard them.
h = h.replace("=", "")
return h
def _get_project_dir_name(self, manifest: ManifestParser) -> str:
if manifest.is_first_party_project():
return manifest.name
else:
project_hash: str = self.get_project_hash(manifest)
return "%s-%s" % (manifest.name, project_hash)
def get_project_install_dir(self, manifest: ManifestParser) -> str:
override = self._install_dir_overrides.get(manifest.name)
if override:
return override
project_dir_name: str = self._get_project_dir_name(manifest)
return os.path.join(self.build_opts.install_dir, project_dir_name)
def get_project_build_dir(self, manifest: ManifestParser) -> str:
override = self._build_dir_overrides.get(manifest.name)
if override:
return override
project_dir_name: str = self._get_project_dir_name(manifest)
return os.path.join(self.build_opts.scratch_dir, "build", project_dir_name)
def get_project_install_prefix(self, manifest: ManifestParser) -> str | None:
return self._install_prefix_overrides.get(manifest.name)
def get_project_install_dir_respecting_install_prefix(
self, manifest: ManifestParser
) -> str:
inst_dir: str = self.get_project_install_dir(manifest)
prefix: str | None = self.get_project_install_prefix(manifest)
if prefix:
return inst_dir + prefix
return inst_dir
================================================
FILE: build/fbcode_builder/getdeps/manifest.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import configparser
import hashlib
import io
import os
import sys
import typing
from .builder import (
AutoconfBuilder,
Boost,
CMakeBootStrapBuilder,
CMakeBuilder,
Iproute2Builder,
MakeBuilder,
MesonBuilder,
NinjaBootstrap,
NopBuilder,
OpenSSLBuilder,
SetupPyBuilder,
SqliteBuilder,
)
from .cargo import CargoBuilder
from .expr import ExprNode, parse_expr
from .fetcher import (
ArchiveFetcher,
GitFetcher,
PreinstalledNopFetcher,
ShipitTransformerFetcher,
SimpleShipitTransformerFetcher,
SubFetcher,
SystemPackageFetcher,
)
from .py_wheel_builder import PythonWheelBuilder
if typing.TYPE_CHECKING:
from .builder import BuilderBase
from .buildopts import BuildOptions
from .fetcher import Fetcher
from .load import ManifestLoader
REQUIRED: str = "REQUIRED"
OPTIONAL: str = "OPTIONAL"
SCHEMA: dict[str, dict[str, object]] = {
"manifest": {
"optional_section": False,
"fields": {
"name": REQUIRED,
"fbsource_path": OPTIONAL,
"shipit_project": OPTIONAL,
"shipit_fbcode_builder": OPTIONAL,
"use_shipit": OPTIONAL,
"shipit_external_branch": OPTIONAL,
"shipit_strip_marker": OPTIONAL,
},
},
"dependencies": {"optional_section": True, "allow_values": False},
"depends.environment": {"optional_section": True},
"git": {
"optional_section": True,
"fields": {
"repo_url": REQUIRED,
"rev": OPTIONAL,
"depth": OPTIONAL,
"branch": OPTIONAL,
},
},
"download": {
"optional_section": True,
"fields": {"url": REQUIRED, "sha256": REQUIRED},
},
"build": {
"optional_section": True,
"fields": {
"builder": REQUIRED,
"subdir": OPTIONAL,
"make_binary": OPTIONAL,
"build_in_src_dir": OPTIONAL,
"only_install": OPTIONAL,
"job_weight_mib": OPTIONAL,
"patchfile": OPTIONAL,
"patchfile_opts": OPTIONAL,
"rewrite_includes": OPTIONAL,
},
},
"msbuild": {"optional_section": True, "fields": {"project": REQUIRED}},
"cargo": {
"optional_section": True,
"fields": {
"build_doc": OPTIONAL,
"workspace_dir": OPTIONAL,
"manifests_to_build": OPTIONAL,
# Where to write cargo config (defaults to build_dir/.cargo/config.toml)
"cargo_config_file": OPTIONAL,
},
},
"github.actions": {
"optional_section": True,
"fields": {
"run_tests": OPTIONAL,
"required_locales": OPTIONAL,
"rust_version": OPTIONAL,
"build_type": OPTIONAL,
},
},
"crate.pathmap": {"optional_section": True},
"cmake.defines": {"optional_section": True},
"autoconf.args": {"optional_section": True},
"autoconf.envcmd.LDFLAGS": {"optional_section": True},
"rpms": {"optional_section": True},
"debs": {"optional_section": True},
"homebrew": {"optional_section": True},
"pps": {"optional_section": True},
"preinstalled.env": {"optional_section": True},
"bootstrap.args": {"optional_section": True},
"b2.args": {"optional_section": True},
"make.build_args": {"optional_section": True},
"make.install_args": {"optional_section": True},
"make.test_args": {"optional_section": True},
"meson.setup_args": {"optional_section": True},
"header-only": {"optional_section": True, "fields": {"includedir": REQUIRED}},
"shipit.pathmap": {"optional_section": True},
"shipit.strip": {"optional_section": True},
"install.files": {"optional_section": True},
"subprojects": {"optional_section": True},
# fb-only
"sandcastle": {"optional_section": True, "fields": {"run_tests": OPTIONAL}},
"setup-py.test": {"optional_section": True, "fields": {"python_script": REQUIRED}},
"setup-py.env": {"optional_section": True},
}
# These sections are allowed to vary for different platforms
# using the expression syntax to enable/disable sections
ALLOWED_EXPR_SECTIONS: list[str] = [
"autoconf.args",
"autoconf.envcmd.LDFLAGS",
"build",
"cmake.defines",
"dependencies",
"make.build_args",
"make.install_args",
"bootstrap.args",
"b2.args",
"download",
"git",
"install.files",
"rpms",
"debs",
"shipit.pathmap",
"shipit.strip",
"homebrew",
"github.actions",
"pps",
]
def parse_conditional_section_name(name: str, section_def: str) -> ExprNode:
expr = name[len(section_def) + 1 :]
return parse_expr(expr, ManifestContext.ALLOWED_VARIABLES)
def validate_allowed_fields(
file_name: str,
section: str,
config: configparser.RawConfigParser,
allowed_fields: dict[str, str],
) -> None:
for field in config.options(section):
if not allowed_fields.get(field):
raise Exception(
("manifest file %s section '%s' contains " "unknown field '%s'")
% (file_name, section, field)
)
for field in allowed_fields:
if allowed_fields[field] == REQUIRED and not config.has_option(section, field):
raise Exception(
("manifest file %s section '%s' is missing " "required field '%s'")
% (file_name, section, field)
)
def validate_allow_values(
file_name: str, section: str, config: configparser.RawConfigParser
) -> None:
for field in config.options(section):
value = config.get(section, field)
if value is not None:
raise Exception(
(
"manifest file %s section '%s' has '%s = %s' but "
"this section doesn't allow specifying values "
"for its entries"
)
% (file_name, section, field, value)
)
def validate_section(
file_name: str, section: str, config: configparser.RawConfigParser
) -> str:
section_def = SCHEMA.get(section)
if not section_def:
for name in ALLOWED_EXPR_SECTIONS:
if section.startswith(name + "."):
# Verify that the conditional parses, but discard it
try:
parse_conditional_section_name(section, name)
except Exception as exc:
raise Exception(
("manifest file %s section '%s' has invalid " "conditional: %s")
% (file_name, section, str(exc))
)
section_def = SCHEMA.get(name)
canonical_section_name = name
break
if not section_def:
raise Exception(
"manifest file %s contains unknown section '%s'" % (file_name, section)
)
else:
canonical_section_name = section
allowed_fields = section_def.get("fields")
if allowed_fields:
# pyre-ignore[6]: Expected `dict[str, str]` but got `object`.
validate_allowed_fields(file_name, section, config, allowed_fields)
elif not section_def.get("allow_values", True):
validate_allow_values(file_name, section, config)
# pyre-fixme[61]: `canonical_section_name` is undefined, or not always defined.
return canonical_section_name
class ManifestParser:
def __init__(self, file_name: str, fp: str | typing.IO[str] | None = None) -> None:
# allow_no_value enables listing parameters in the
# autoconf.args section one per line
config = configparser.RawConfigParser(allow_no_value=True)
config.optionxform = str # type: ignore[assignment] # make it case sensitive
if fp is None:
with open(file_name, "r") as fp:
config.read_file(fp)
elif isinstance(fp, type("")):
# For testing purposes, parse from a string (str
# or unicode)
config.read_file(io.StringIO(fp))
else:
config.read_file(fp)
# validate against the schema
seen_sections: set[str] = set()
for section in config.sections():
seen_sections.add(validate_section(file_name, section, config))
for section in SCHEMA.keys():
section_def = SCHEMA[section]
if (
not section_def.get("optional_section", False)
and section not in seen_sections
):
raise Exception(
"manifest file %s is missing required section %s"
% (file_name, section)
)
self._config: configparser.RawConfigParser = config
self.name: str = config.get("manifest", "name")
self.fbsource_path: str | None = self.get("manifest", "fbsource_path")
self.shipit_project: str | None = self.get("manifest", "shipit_project")
self.shipit_fbcode_builder: str | None = self.get(
"manifest", "shipit_fbcode_builder"
)
self.resolved_system_packages: dict[str, str] = {}
self.shipit_strip_marker: str | None = self.get(
"manifest", "shipit_strip_marker", defval="@fb-only"
)
if self.name != os.path.basename(file_name):
raise Exception(
"filename of the manifest '%s' does not match the manifest name '%s'"
% (file_name, self.name)
)
if "." in self.name:
raise Exception(
f"manifest name ({self.name}) must not contain the '.' character (it is incompatible with github actions)"
)
def get(
self,
section: str,
key: str,
defval: str | None = None,
ctx: ManifestContext | dict[str, str | None] | None = None,
) -> str | None:
ctx = ctx or {}
for s in self._config.sections():
if s == section:
if self._config.has_option(s, key):
return self._config.get(s, key)
return defval
if s.startswith(section + "."):
expr = parse_conditional_section_name(s, section)
# pyre-fixme[6]: For 1st argument expected `Dict[str,
# Optional[str]]` but got `Union[Dict[str, Optional[str]],
# ManifestContext]`.
if not expr.eval(ctx):
continue
if self._config.has_option(s, key):
return self._config.get(s, key)
return defval
def get_dependencies(self, ctx: ManifestContext) -> list[str]:
dep_list = list(self.get_section_as_dict("dependencies", ctx).keys())
dep_list.sort()
builder = self.get("build", "builder", ctx=ctx)
if builder in ("cmake", "python-wheel"):
dep_list.insert(0, "cmake")
elif builder == "autoconf" and self.name not in (
"autoconf",
"libtool",
"automake",
):
# they need libtool and its deps (automake, autoconf) so add
# those as deps (but obviously not if we're building those
# projects themselves)
dep_list.insert(0, "libtool")
return dep_list
def get_section_as_args(
self,
section: str,
ctx: ManifestContext | dict[str, str | None] | None = None,
) -> list[str]:
"""Intended for use with the make.[build_args/install_args] and
autoconf.args sections, this method collects the entries and returns an
array of strings.
If the manifest contains conditional sections, ctx is used to
evaluate the condition and merge in the values.
"""
args: list[str] = []
ctx = ctx or {}
for s in self._config.sections():
if s != section:
if not s.startswith(section + "."):
continue
expr = parse_conditional_section_name(s, section)
# pyre-fixme[6]: For 1st argument expected `Dict[str,
# Optional[str]]` but got `Union[Dict[str, Optional[str]],
# ManifestContext]`.
if not expr.eval(ctx):
continue
for field in self._config.options(s):
value = self._config.get(s, field)
if value is None:
args.append(field)
else:
args.append("%s=%s" % (field, value))
return args
def get_section_as_ordered_pairs(
self,
section: str,
ctx: ManifestContext | dict[str, str | None] | None = None,
) -> list[tuple[str, str | None]]:
"""Used for eg: shipit.pathmap which has strong
ordering requirements"""
res: list[tuple[str, str | None]] = []
ctx = ctx or {}
for s in self._config.sections():
if s != section:
if not s.startswith(section + "."):
continue
expr = parse_conditional_section_name(s, section)
# pyre-fixme[6]: For 1st argument expected `Dict[str,
# Optional[str]]` but got `Union[Dict[str, Optional[str]],
# ManifestContext]`.
if not expr.eval(ctx):
continue
for key in self._config.options(s):
value = self._config.get(s, key)
res.append((key, value))
return res
def get_section_as_dict(
self,
section: str,
ctx: ManifestContext | dict[str, str | None] | None,
) -> dict[str, str | None]:
d: dict[str, str | None] = {}
for s in self._config.sections():
if s != section:
if not s.startswith(section + "."):
continue
expr = parse_conditional_section_name(s, section)
# pyre-fixme[6]: For 1st argument expected `Dict[str,
# Optional[str]]` but got `Union[None, Dict[str, Optional[str]],
# ManifestContext]`.
if not expr.eval(ctx):
continue
for field in self._config.options(s):
value = self._config.get(s, field)
d[field] = value
return d
def update_hash(self, hasher: hashlib._Hash, ctx: ManifestContext) -> None:
"""Compute a hash over the configuration for the given
context. The goal is for the hash to change if the config
for that context changes, but not if a change is made to
the config only for a different platform than that expressed
by ctx. The hash is intended to be used to help invalidate
a future cache for the third party build products.
The hasher argument is a hash object returned from hashlib."""
for section in sorted(SCHEMA.keys()):
hasher.update(section.encode("utf-8"))
# Note: at the time of writing, nothing in the implementation
# relies on keys in any config section being ordered.
# In theory we could have conflicting flags in different
# config sections and later flags override earlier flags.
# For the purposes of computing a hash we're not super
# concerned about this: manifest changes should be rare
# enough and we'd rather that this trigger an invalidation
# than strive for a cache hit at this time.
pairs = self.get_section_as_ordered_pairs(section, ctx)
pairs.sort(key=lambda pair: pair[0])
for key, value in pairs:
hasher.update(key.encode("utf-8"))
if value is not None:
hasher.update(value.encode("utf-8"))
def is_first_party_project(self) -> bool:
"""returns true if this is an FB first-party project"""
return self.shipit_project is not None
def get_required_system_packages(
self, ctx: ManifestContext
) -> dict[str, list[str]]:
"""Returns dictionary of packager system -> list of packages"""
return {
"rpm": self.get_section_as_args("rpms", ctx),
"deb": self.get_section_as_args("debs", ctx),
"homebrew": self.get_section_as_args("homebrew", ctx),
"pacman-package": self.get_section_as_args("pps", ctx),
}
def _is_satisfied_by_preinstalled_environment(self, ctx: ManifestContext) -> bool:
envs = self.get_section_as_args("preinstalled.env", ctx)
if not envs:
return False
for key in envs:
val = os.environ.get(key, None)
print(
f"Testing ENV[{key}]: {repr(val)}",
file=sys.stderr,
)
if val is None:
return False
if len(val) == 0:
return False
return True
def get_repo_url(self, ctx: ManifestContext) -> str | None:
return self.get("git", "repo_url", ctx=ctx)
def _create_fetcher(
self, build_options: BuildOptions, ctx: ManifestContext
) -> Fetcher:
real_shipit_available = ShipitTransformerFetcher.available(build_options)
use_real_shipit = real_shipit_available and (
build_options.use_shipit
or self.get("manifest", "use_shipit", defval="false", ctx=ctx) == "true"
)
if (
not use_real_shipit
and self.fbsource_path
and build_options.fbsource_dir
and self.shipit_project
):
return SimpleShipitTransformerFetcher(build_options, self, ctx)
if (
self.fbsource_path
and build_options.fbsource_dir
and self.shipit_project
and real_shipit_available
):
# We can use the code from fbsource
return ShipitTransformerFetcher(
build_options,
self.shipit_project,
# pyre-fixme[6]: For 3rd argument expected `str` but got
# `Optional[str]`.
self.get("manifest", "shipit_external_branch"),
)
# If both of these are None, the package can only be coming from
# preinstalled toolchain or system packages
repo_url = self.get_repo_url(ctx)
url = self.get("download", "url", ctx=ctx)
# Can we satisfy this dep with system packages?
if (repo_url is None and url is None) or build_options.allow_system_packages:
if self._is_satisfied_by_preinstalled_environment(ctx):
# pyre-fixme[7]: Expected `Fetcher` but got `PreinstalledNopFetcher`.
return PreinstalledNopFetcher()
if build_options.host_type.get_package_manager():
packages = self.get_required_system_packages(ctx)
package_fetcher = SystemPackageFetcher(build_options, packages)
if package_fetcher.packages_are_installed():
# pyre-fixme[7]: Expected `Fetcher` but got `SystemPackageFetcher`.
return package_fetcher
if repo_url:
rev = self.get("git", "rev")
depth = self.get("git", "depth")
branch = self.get("git", "branch")
# pyre-fixme[6]: For 4th argument expected `str` but got `Optional[str]`.
# pyre-fixme[6]: For 5th argument expected `int` but got `Optional[str]`.
# pyre-fixme[6]: For 6th argument expected `str` but got `Optional[str]`.
return GitFetcher(build_options, self, repo_url, rev, depth, branch)
if url:
# We need to defer this import until now to avoid triggering
# a cycle when the facebook/__init__.py is loaded.
try:
from .facebook.lfs import LFSCachingArchiveFetcher
return LFSCachingArchiveFetcher(
build_options,
self,
url,
# pyre-fixme[6]: For 4th argument expected `str` but got
# `Optional[str]`.
self.get("download", "sha256", ctx=ctx),
)
except ImportError:
# This FB internal module isn't shippped to github,
# so just use its base class
return ArchiveFetcher(
build_options,
self,
url,
# pyre-fixme[6]: For 4th argument expected `str` but got
# `Optional[str]`.
self.get("download", "sha256", ctx=ctx),
)
raise KeyError(
f"project {self.name} has no fetcher configuration or system packages matching {ctx} - have you run `getdeps.py install-system-deps --recursive`?"
)
def create_fetcher(
self,
build_options: BuildOptions,
loader: ManifestLoader,
ctx: ManifestContext,
) -> Fetcher:
fetcher = self._create_fetcher(build_options, ctx)
subprojects = self.get_section_as_ordered_pairs("subprojects", ctx)
if subprojects:
subs: list[tuple[Fetcher, str | None]] = []
for project, subdir in subprojects:
submanifest = loader.load_manifest(project)
subfetcher = submanifest.create_fetcher(build_options, loader, ctx)
subs.append((subfetcher, subdir))
# pyre-fixme[6]: For 2nd argument expected `List[Tuple[Fetcher, str]]`
# but got `List[Tuple[Fetcher, Optional[str]]]`.
return SubFetcher(fetcher, subs)
else:
return fetcher
def get_builder_name(self, ctx: ManifestContext) -> str:
builder = self.get("build", "builder", ctx=ctx)
if not builder:
raise Exception("project %s has no builder for %r" % (self.name, ctx))
return builder
def create_builder( # noqa:C901
self,
build_options: BuildOptions,
src_dir: str,
build_dir: str,
inst_dir: str,
ctx: ManifestContext,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
final_install_prefix: str | None = None,
extra_cmake_defines: dict[str, str] | None = None,
cmake_targets: list[str] | None = None,
extra_b2_args: list[str] | None = None,
) -> BuilderBase:
builder = self.get_builder_name(ctx)
build_in_src_dir = self.get("build", "build_in_src_dir", "false", ctx=ctx)
if build_in_src_dir == "true":
# Some scripts don't work when they are configured and build in
# a different directory than source (or when the build directory
# is not a subdir of source).
build_dir = src_dir
subdir = self.get("build", "subdir", None, ctx=ctx)
if subdir is not None:
build_dir = os.path.join(build_dir, subdir)
print("build_dir is %s" % build_dir) # just to quiet lint
if builder == "make" or builder == "cmakebootstrap":
build_args = self.get_section_as_args("make.build_args", ctx)
install_args = self.get_section_as_args("make.install_args", ctx)
test_args = self.get_section_as_args("make.test_args", ctx)
if builder == "cmakebootstrap":
return CMakeBootStrapBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
# pyre-fixme[6]: For 7th argument expected `str` but got `None`.
None,
inst_dir,
build_args,
install_args,
test_args,
)
else:
return MakeBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
# pyre-fixme[6]: For 7th argument expected `str` but got `None`.
None,
inst_dir,
build_args,
install_args,
test_args,
)
if builder == "autoconf":
args = self.get_section_as_args("autoconf.args", ctx)
conf_env_args: dict[str, list[str]] = {}
ldflags_cmd = self.get_section_as_args("autoconf.envcmd.LDFLAGS", ctx)
if ldflags_cmd:
conf_env_args["LDFLAGS"] = ldflags_cmd
return AutoconfBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
args,
conf_env_args,
)
if builder == "boost":
args = self.get_section_as_args("b2.args", ctx)
if extra_b2_args is not None:
args += extra_b2_args
return Boost(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
args,
)
if builder == "cmake":
defines = self.get_section_as_dict("cmake.defines", ctx)
return CMakeBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
# pyre-fixme[6]: For 9th argument expected `Optional[Dict[str,
# str]]` but got `Dict[str, Optional[str]]`.
defines,
final_install_prefix,
extra_cmake_defines,
cmake_targets,
)
if builder == "python-wheel":
return PythonWheelBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
)
if builder == "sqlite":
return SqliteBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
)
if builder == "ninja_bootstrap":
return NinjaBootstrap(
loader,
dep_manifests,
build_options,
ctx,
self,
build_dir,
src_dir,
inst_dir,
)
if builder == "nop":
return NopBuilder(
loader, dep_manifests, build_options, ctx, self, src_dir, inst_dir
)
if builder == "openssl":
return OpenSSLBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
build_dir,
src_dir,
inst_dir,
)
if builder == "iproute2":
return Iproute2Builder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
)
if builder == "meson":
return MesonBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
)
if builder == "setup-py":
return SetupPyBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
)
if builder == "cargo":
return self.create_cargo_builder(
loader,
dep_manifests,
build_options,
ctx,
src_dir,
build_dir,
inst_dir,
)
raise KeyError("project %s has no known builder" % (self.name))
def create_prepare_builders(
self,
build_options: BuildOptions,
ctx: ManifestContext,
src_dir: str,
build_dir: str,
inst_dir: str,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
) -> list[BuilderBase]:
"""Create builders that have a prepare step run, e.g. to write config files"""
prepare_builders: list[BuilderBase] = []
builder = self.get_builder_name(ctx)
cargo = self.get_section_as_dict("cargo", ctx)
if not builder == "cargo" and cargo:
cargo_builder = self.create_cargo_builder(
loader,
dep_manifests,
build_options,
ctx,
src_dir,
build_dir,
inst_dir,
)
prepare_builders.append(cargo_builder)
return prepare_builders
def create_cargo_builder(
self,
loader: ManifestLoader,
dep_manifests: list[ManifestParser],
build_options: BuildOptions,
ctx: ManifestContext,
src_dir: str,
build_dir: str,
inst_dir: str,
) -> CargoBuilder:
# pyre-fixme[6]: For 3rd argument expected `Optional[str]` but got `bool`.
build_doc = self.get("cargo", "build_doc", False, ctx)
workspace_dir = self.get("cargo", "workspace_dir", None, ctx)
manifests_to_build = self.get("cargo", "manifests_to_build", None, ctx)
cargo_config_file = self.get("cargo", "cargo_config_file", None, ctx)
return CargoBuilder(
loader,
dep_manifests,
build_options,
ctx,
self,
src_dir,
build_dir,
inst_dir,
# pyre-fixme[6]: For 9th argument expected `bool` but got `Optional[str]`.
build_doc,
workspace_dir,
manifests_to_build,
cargo_config_file,
)
class ManifestContext:
"""ProjectContext contains a dictionary of values to use when evaluating boolean
expressions in a project manifest.
This object should be passed as the `ctx` parameter in ManifestParser.get() calls.
"""
ALLOWED_VARIABLES: set[str] = {
"os",
"distro",
"distro_vers",
"fb",
"fbsource",
"test",
"shared_libs",
}
def __init__(self, ctx_dict: dict[str, str | None]) -> None:
assert set(ctx_dict.keys()) == self.ALLOWED_VARIABLES
self.ctx_dict: dict[str, str | None] = ctx_dict
def get(self, key: str) -> str | None:
return self.ctx_dict[key]
def set(self, key: str, value: str | None) -> None:
assert key in self.ALLOWED_VARIABLES
self.ctx_dict[key] = value
def copy(self) -> ManifestContext:
return ManifestContext(dict(self.ctx_dict))
def __str__(self) -> str:
s = ", ".join(
"%s=%s" % (key, value) for key, value in sorted(self.ctx_dict.items())
)
return "{" + s + "}"
class ContextGenerator:
"""ContextGenerator allows creating ManifestContext objects on a per-project basis.
This allows us to evaluate different projects with slightly different contexts.
For instance, this can be used to only enable tests for some projects."""
def __init__(self, default_ctx: dict[str, str | None]) -> None:
self.default_ctx: ManifestContext = ManifestContext(default_ctx)
self.ctx_by_project: dict[str, ManifestContext] = {}
def set_value_for_project(
self, project_name: str, key: str, value: str | None
) -> None:
project_ctx = self.ctx_by_project.get(project_name)
if project_ctx is None:
project_ctx = self.default_ctx.copy()
self.ctx_by_project[project_name] = project_ctx
project_ctx.set(key, value)
def set_value_for_all_projects(self, key: str, value: str | None) -> None:
self.default_ctx.set(key, value)
for ctx in self.ctx_by_project.values():
ctx.set(key, value)
def get_context(self, project_name: str) -> ManifestContext:
return self.ctx_by_project.get(project_name, self.default_ctx)
================================================
FILE: build/fbcode_builder/getdeps/platform.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import os
import platform
import re
import shlex
import sys
def is_windows() -> bool:
"""Returns true if the system we are currently running on
is a Windows system"""
return sys.platform.startswith("win")
def get_linux_type() -> tuple[str | None, str | None, str | None]:
try:
with open("/etc/os-release") as f:
data = f.read()
except EnvironmentError:
return (None, None, None)
os_vars: dict[str, str] = {}
for line in data.splitlines():
parts = line.split("=", 1)
if len(parts) != 2:
continue
key = parts[0].strip()
value_parts = shlex.split(parts[1].strip())
if not value_parts:
value = ""
else:
value = value_parts[0]
os_vars[key] = value
name = os_vars.get("NAME")
if name:
name = name.lower()
name = re.sub("linux", "", name)
name = name.strip().replace(" ", "_")
version_id = os_vars.get("VERSION_ID")
if version_id:
version_id = version_id.lower()
return "linux", name, version_id
# Ideally we'd use a common library like `psutil` to read system information,
# but getdeps can't take third-party dependencies.
def _get_available_ram_linux() -> int:
# TODO: Ideally, this function would inspect the current cgroup for any
# limits, rather than solely relying on system RAM.
meminfo_path = "/proc/meminfo"
try:
with open(meminfo_path) as f:
for line in f:
try:
key, value = line.split(":", 1)
except ValueError:
continue
suffix = " kB\n"
if key == "MemAvailable" and value.endswith(suffix):
value = value[: -len(suffix)]
try:
return int(value) // 1024
except ValueError:
continue
except OSError:
print("error opening {}".format(meminfo_path), end="", file=sys.stderr)
else:
print(
"{} had no valid MemAvailable".format(meminfo_path), end="", file=sys.stderr
)
guess = 8
print(", guessing {} GiB".format(guess), file=sys.stderr)
return guess * 1024
def _get_available_ram_macos() -> int:
import ctypes.util
libc = ctypes.CDLL(ctypes.util.find_library("libc"), use_errno=True)
sysctlbyname = libc.sysctlbyname
sysctlbyname.restype = ctypes.c_int
sysctlbyname.argtypes = [
ctypes.c_char_p,
ctypes.c_void_p,
ctypes.POINTER(ctypes.c_size_t),
ctypes.c_void_p,
ctypes.c_size_t,
]
# TODO: There may be some way to approximate an availability
# metric, but just use total RAM for now.
memsize = ctypes.c_int64()
memsizesize = ctypes.c_size_t(8)
res = sysctlbyname(
b"hw.memsize", ctypes.byref(memsize), ctypes.byref(memsizesize), None, 0
)
if res != 0:
raise NotImplementedError(
f"failed to retrieve hw.memsize sysctl: {ctypes.get_errno()}"
)
return memsize.value // (1024 * 1024)
def _get_available_ram_windows() -> int:
import ctypes
DWORD = ctypes.c_uint32
QWORD = ctypes.c_uint64
class MEMORYSTATUSEX(ctypes.Structure):
_fields_ = [
("dwLength", DWORD),
("dwMemoryLoad", DWORD),
("ullTotalPhys", QWORD),
("ullAvailPhys", QWORD),
("ullTotalPageFile", QWORD),
("ullAvailPageFile", QWORD),
("ullTotalVirtual", QWORD),
("ullAvailVirtual", QWORD),
("ullExtendedVirtual", QWORD),
]
ms = MEMORYSTATUSEX()
ms.dwLength = ctypes.sizeof(ms)
# pyre-ignore[16]
res = ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(ms))
if res == 0:
raise NotImplementedError("error calling GlobalMemoryStatusEx")
# This is fuzzy, but AvailPhys is too conservative, and AvailTotal is too
# aggressive, so average the two. It's okay for builds to use some swap.
return (ms.ullAvailPhys + ms.ullTotalPhys) // (2 * 1024 * 1024)
def _get_available_ram_freebsd() -> int:
import ctypes.util
libc = ctypes.CDLL(ctypes.util.find_library("libc"), use_errno=True)
sysctlbyname = libc.sysctlbyname
sysctlbyname.restype = ctypes.c_int
sysctlbyname.argtypes = [
ctypes.c_char_p,
ctypes.c_void_p,
ctypes.POINTER(ctypes.c_size_t),
ctypes.c_void_p,
ctypes.c_size_t,
]
# hw.usermem is pretty close to what we want.
memsize = ctypes.c_int64()
memsizesize = ctypes.c_size_t(8)
res = sysctlbyname(
b"hw.usermem", ctypes.byref(memsize), ctypes.byref(memsizesize), None, 0
)
if res != 0:
raise NotImplementedError(
f"failed to retrieve hw.memsize sysctl: {ctypes.get_errno()}"
)
return memsize.value // (1024 * 1024)
def get_available_ram() -> int:
"""
Returns a platform-appropriate available RAM metric in MiB.
"""
if sys.platform == "linux":
return _get_available_ram_linux()
elif sys.platform == "darwin":
return _get_available_ram_macos()
elif sys.platform == "win32":
return _get_available_ram_windows()
elif sys.platform.startswith("freebsd"):
return _get_available_ram_freebsd()
else:
raise NotImplementedError(
f"platform {sys.platform} does not have an implementation of get_available_ram"
)
def is_current_host_arm() -> bool:
if sys.platform.startswith("darwin"):
# platform.machine() can be fooled by rosetta for python < 3.9.2
return "ARM64" in os.uname().version
else:
machine = platform.machine().lower()
return "arm" in machine or "aarch" in machine
class HostType:
def __init__(
self,
ostype: str | None = None,
distro: str | None = None,
distrovers: str | None = None,
) -> None:
# Maybe we should allow callers to indicate whether this machine uses
# an ARM architecture, but we need to change HostType serialization
# and deserialization in that case and hunt down anywhere that is
# persisting that serialized data.
isarm = False
if ostype is None:
distro = None
distrovers = None
if sys.platform.startswith("linux"):
ostype, distro, distrovers = get_linux_type()
elif sys.platform.startswith("darwin"):
ostype = "darwin"
elif is_windows():
ostype = "windows"
distrovers = str(sys.getwindowsversion().major)
elif sys.platform.startswith("freebsd"):
ostype = "freebsd"
else:
ostype = sys.platform
isarm = is_current_host_arm()
# The operating system type
self.ostype: str | None = ostype
# The distribution, if applicable
self.distro: str | None = distro
# The OS/distro version if known
self.distrovers: str | None = distrovers
# Does the CPU use an ARM architecture? ARM includes Apple Silicon
# Macs as well as other ARM systems that might be running Linux or
# something.
self.isarm: bool = isarm
def is_windows(self) -> bool:
return self.ostype == "windows"
# is_arm is kinda half implemented at the moment. This method is only
# intended to be used when HostType represents information about the
# current machine we are running on.
# When HostType is being used to enumerate platform types (represent
# information about machine types that we may or may not be running on)
# the result could be nonsense (under the current implementation its always
# false.)
def is_arm(self) -> bool:
return self.isarm
def is_darwin(self) -> bool:
return self.ostype == "darwin"
def is_linux(self) -> bool:
return self.ostype == "linux"
def is_freebsd(self) -> bool:
return self.ostype == "freebsd"
def as_tuple_string(self) -> str:
return "%s-%s-%s" % (
self.ostype,
self.distro or "none",
self.distrovers or "none",
)
def get_package_manager(self) -> str | None:
if not self.is_linux() and not self.is_darwin():
return None
if self.is_darwin():
return "homebrew"
if self.distro in ("fedora", "centos", "centos_stream", "rocky"):
return "rpm"
if self.distro is not None and self.distro.startswith(
("debian", "ubuntu", "pop!_os", "mint")
):
return "deb"
if self.distro == "arch":
return "pacman-package"
return None
@staticmethod
def from_tuple_string(s: str) -> HostType:
ostype, distro, distrovers = s.split("-")
return HostType(ostype=ostype, distro=distro, distrovers=distrovers)
def __eq__(self, b: object) -> bool:
if not isinstance(b, HostType):
return False
return (
self.ostype == b.ostype
and self.distro == b.distro
and self.distrovers == b.distrovers
)
================================================
FILE: build/fbcode_builder/getdeps/py_wheel_builder.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import codecs
import collections
import email
import email.message
import os
import re
import stat
from .builder import BuilderBase, CMakeBuilder
WheelNameInfo = collections.namedtuple(
"WheelNameInfo", ("distribution", "version", "build", "python", "abi", "platform")
)
CMAKE_HEADER = """
cmake_minimum_required(VERSION 3.8)
project("{manifest_name}" LANGUAGES C)
set(CMAKE_MODULE_PATH
"{cmake_dir}"
${{CMAKE_MODULE_PATH}}
)
include(FBPythonBinary)
set(CMAKE_INSTALL_DIR lib/cmake/{manifest_name} CACHE STRING
"The subdirectory where CMake package config files should be installed")
"""
CMAKE_FOOTER = """
install_fb_python_library({lib_name} EXPORT all)
install(
EXPORT all
FILE {manifest_name}-targets.cmake
NAMESPACE {namespace}::
DESTINATION ${{CMAKE_INSTALL_DIR}}
)
include(CMakePackageConfigHelpers)
configure_package_config_file(
${{CMAKE_BINARY_DIR}}/{manifest_name}-config.cmake.in
{manifest_name}-config.cmake
INSTALL_DESTINATION ${{CMAKE_INSTALL_DIR}}
PATH_VARS
CMAKE_INSTALL_DIR
)
install(
FILES ${{CMAKE_CURRENT_BINARY_DIR}}/{manifest_name}-config.cmake
DESTINATION ${{CMAKE_INSTALL_DIR}}
)
"""
CMAKE_CONFIG_FILE = """
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
set_and_check({upper_name}_CMAKE_DIR "@PACKAGE_CMAKE_INSTALL_DIR@")
if (NOT TARGET {namespace}::{lib_name})
include("${{{upper_name}_CMAKE_DIR}}/{manifest_name}-targets.cmake")
endif()
set({upper_name}_LIBRARIES {namespace}::{lib_name})
{find_dependency_lines}
if (NOT {manifest_name}_FIND_QUIETLY)
message(STATUS "Found {manifest_name}: ${{PACKAGE_PREFIX_DIR}}")
endif()
"""
# Note: for now we are manually manipulating the wheel packet contents.
# The wheel format is documented here:
# https://www.python.org/dev/peps/pep-0491/#file-format
#
# We currently aren't particularly smart about correctly handling the full wheel
# functionality, but this is good enough to handle simple pure-python wheels,
# which is the main thing we care about right now.
#
# We could potentially use pip to install the wheel to a temporary location and
# then copy its "installed" files, but this has its own set of complications.
# This would require pip to already be installed and available, and we would
# need to correctly find the right version of pip or pip3 to use.
# If we did ever want to go down that path, we would probably want to use
# something like the following pip3 command:
# pip3 --isolated install --no-cache-dir --no-index --system \
# --target
class PythonWheelBuilder(BuilderBase):
"""This Builder can take Python wheel archives and install them as python libraries
that can be used by add_fb_python_library()/add_fb_python_executable() CMake rules.
"""
# pyre-fixme[13]: Attribute `dist_info_dir` is never initialized.
dist_info_dir: str
# pyre-fixme[13]: Attribute `template_format_dict` is never initialized.
template_format_dict: dict[str, str]
def _build(self, reconfigure: bool) -> None:
# When we are invoked, self.src_dir contains the unpacked wheel contents.
#
# Since a wheel file is just a zip file, the Fetcher code recognizes it as such
# and goes ahead and unpacks it. (We could disable that Fetcher behavior in the
# future if we ever wanted to, say if we wanted to call pip here.)
wheel_name = self._parse_wheel_name()
name_version_prefix = "-".join((wheel_name.distribution, wheel_name.version))
dist_info_name = name_version_prefix + ".dist-info"
data_dir_name = name_version_prefix + ".data"
self.dist_info_dir = os.path.join(self.src_dir, dist_info_name)
wheel_metadata = self._read_wheel_metadata(wheel_name)
# Check that we can understand the wheel version.
# We don't really care about wheel_metadata["Root-Is-Purelib"] since
# we are generating our own standalone python archives rather than installing
# into site-packages.
version = wheel_metadata["Wheel-Version"]
if not version.startswith("1."):
raise Exception("unsupported wheel version %s" % (version,))
# Add a find_dependency() call for each of our dependencies.
# The dependencies are also listed in the wheel METADATA file, but it is simpler
# to pull this directly from the getdeps manifest.
dep_list = sorted(
self.manifest.get_section_as_dict("dependencies", self.ctx).keys()
)
find_dependency_lines = ["find_dependency({})".format(dep) for dep in dep_list]
getdeps_cmake_dir = os.path.join(
os.path.dirname(os.path.dirname(__file__)), "CMake"
)
self.template_format_dict = {
# Note that CMake files always uses forward slash separators in path names,
# even on Windows. Therefore replace path separators here.
"cmake_dir": _to_cmake_path(getdeps_cmake_dir),
"lib_name": self.manifest.name,
"manifest_name": self.manifest.name,
"namespace": self.manifest.name,
"upper_name": self.manifest.name.upper().replace("-", "_"),
"find_dependency_lines": "\n".join(find_dependency_lines),
}
# Find sources from the root directory
path_mapping: dict[str, str] = {}
for entry in os.listdir(self.src_dir):
if entry == data_dir_name:
continue
self._add_sources(path_mapping, os.path.join(self.src_dir, entry), entry)
# Files under the .data directory also need to be installed in the correct
# locations
if os.path.exists(data_dir_name):
# TODO: process the subdirectories of data_dir_name
# This isn't implemented yet since for now we have only needed dependencies
# on some simple pure Python wheels, so I haven't tested against wheels with
# additional files in the .data directory.
raise Exception(
"handling of the subdirectories inside %s is not implemented yet"
% data_dir_name
)
# Emit CMake files
self._write_cmakelists(path_mapping, dep_list)
self._write_cmake_config_template()
# Run the build
self._run_cmake_build(reconfigure)
def _run_cmake_build(self, reconfigure: bool) -> None:
cmake_builder = CMakeBuilder(
loader=self.loader,
dep_manifests=self.dep_manifests,
build_opts=self.build_opts,
ctx=self.ctx,
manifest=self.manifest,
# Note that we intentionally supply src_dir=build_dir,
# since we wrote out our generated CMakeLists.txt in the build directory
src_dir=self.build_dir,
build_dir=self.build_dir,
inst_dir=self.inst_dir,
defines={},
final_install_prefix=None,
)
cmake_builder.build(reconfigure=reconfigure)
def _write_cmakelists(
self, path_mapping: dict[str, str], dependencies: list[str]
) -> None:
cmake_path = os.path.join(self.build_dir, "CMakeLists.txt")
with open(cmake_path, "w") as f:
f.write(CMAKE_HEADER.format(**self.template_format_dict))
for dep in dependencies:
f.write("find_package({0} REQUIRED)\n".format(dep))
f.write(
"add_fb_python_library({lib_name}\n".format(**self.template_format_dict)
)
f.write(' BASE_DIR "%s"\n' % _to_cmake_path(self.src_dir))
f.write(" SOURCES\n")
for src_path, install_path in path_mapping.items():
f.write(
' "%s=%s"\n'
% (_to_cmake_path(src_path), _to_cmake_path(install_path))
)
if dependencies:
f.write(" DEPENDS\n")
for dep in dependencies:
f.write(' "{0}::{0}"\n'.format(dep))
f.write(")\n")
f.write(CMAKE_FOOTER.format(**self.template_format_dict))
def _write_cmake_config_template(self) -> None:
config_path_name = self.manifest.name + "-config.cmake.in"
output_path = os.path.join(self.build_dir, config_path_name)
with open(output_path, "w") as f:
f.write(CMAKE_CONFIG_FILE.format(**self.template_format_dict))
def _add_sources(
self, path_mapping: dict[str, str], src_path: str, install_path: str
) -> None:
s = os.lstat(src_path)
if not stat.S_ISDIR(s.st_mode):
path_mapping[src_path] = install_path
return
for entry in os.listdir(src_path):
self._add_sources(
path_mapping,
os.path.join(src_path, entry),
os.path.join(install_path, entry),
)
def _parse_wheel_name(self) -> WheelNameInfo:
# The ArchiveFetcher prepends "manifest_name-", so strip that off first.
wheel_name = os.path.basename(self.src_dir)
prefix = self.manifest.name + "-"
if not wheel_name.startswith(prefix):
raise Exception(
"expected wheel source directory to be of the form %s-NAME.whl"
% (prefix,)
)
wheel_name = wheel_name[len(prefix) :]
wheel_name_re = re.compile(
r"(?P[^-]+)"
r"-(?P\d+[^-]*)"
r"(-(?P\d+[^-]*))?"
r"-(?P\w+\d+(\.\w+\d+)*)"
r"-(?P\w+)"
r"-(?P\w+(\.\w+)*)"
r"\.whl"
)
match = wheel_name_re.match(wheel_name)
if not match:
raise Exception(
"bad python wheel name %s: expected to have the form "
"DISTRIBUTION-VERSION-[-BUILD]-PYTAG-ABI-PLATFORM"
)
return WheelNameInfo(
distribution=match.group("distribution"),
version=match.group("version"),
build=match.group("build"),
python=match.group("python"),
abi=match.group("abi"),
platform=match.group("platform"),
)
# pyre-fixme[24]: Generic type `email.message.Message` expects 2 type parameters.
def _read_wheel_metadata(self, wheel_name: WheelNameInfo) -> email.message.Message:
metadata_path = os.path.join(self.dist_info_dir, "WHEEL")
with codecs.open(metadata_path, "r", encoding="utf-8") as f:
return email.message_from_file(f)
def _to_cmake_path(path: str) -> str:
# CMake always uses forward slashes to separate paths in CMakeLists.txt files,
# even on Windows. It treats backslashes as character escapes, so using
# backslashes in the path will cause problems. Therefore replace all path
# separators with forward slashes to make sure the paths are correct on Windows.
# e.g. "C:\foo\bar.txt" becomes "C:/foo/bar.txt"
return path.replace(os.path.sep, "/")
================================================
FILE: build/fbcode_builder/getdeps/runcmd.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import os
import select
import subprocess
import sys
from collections.abc import Callable
from shlex import quote as shellquote
from .envfuncs import Env
from .platform import is_windows
class RunCommandError(Exception):
pass
def make_memory_limit_preexec_fn(
job_weight_mib: int,
) -> Callable[[], None] | None:
"""Create a preexec_fn that sets a per-process virtual memory limit.
When getdeps spawns build commands (cmake -> ninja -> N compiler processes),
the parallelism is computed from available RAM divided by job_weight_mib.
However, there is no enforcement of that budget: if a compiler or linker
process exceeds its expected memory usage, the system can run out of RAM
and the Linux OOM killer may terminate arbitrary processes — including the
user's shell or terminal.
This function returns a callable suitable for subprocess.Popen's preexec_fn
parameter. It runs in each child process after fork() but before exec(),
setting RLIMIT_AS (virtual address space limit) so that a runaway process
gets a failed allocation (std::bad_alloc / ENOMEM) instead of triggering
the OOM killer. The limit is inherited by all descendant processes (ninja,
compiler invocations, etc.).
The per-process limit is set to job_weight_mib * 10. The 10x multiplier
accounts for the fact that RLIMIT_AS caps virtual address space, which is
typically 2-4x larger than resident (physical) memory for C++ compilers
due to memory-mapped files, shared libraries, and address space reservations
that don't consume physical RAM. The multiplier is intentionally generous:
the goal is a safety net that catches genuine runaways before the OOM killer
fires, not a tight per-job budget.
Only applies on Linux, where the OOM killer is the problem. Returns None
on other platforms.
"""
if sys.platform != "linux":
return None
# Each job is budgeted job_weight_mib of physical RAM. Virtual address
# space is typically 2-4x RSS. Use 10x as a generous safety net: tight
# enough to stop a runaway process before the OOM killer fires, but loose
# enough to avoid false positives from normal virtual memory overhead.
limit_bytes: int = job_weight_mib * 10 * 1024 * 1024
def _set_memory_limit() -> None:
import resource
resource.setrlimit(resource.RLIMIT_AS, (limit_bytes, limit_bytes))
return _set_memory_limit
def _print_env_diff(env: Env, log_fn: Callable[[str], None]) -> None:
current_keys = set(os.environ.keys())
wanted_env = set(env.keys())
unset_keys = current_keys.difference(wanted_env)
for k in sorted(unset_keys):
log_fn("+ unset %s\n" % k)
added_keys = wanted_env.difference(current_keys)
for k in wanted_env.intersection(current_keys):
if os.environ[k] != env[k]:
added_keys.add(k)
for k in sorted(added_keys):
if ("PATH" in k) and (os.pathsep in env[k]):
log_fn("+ %s=\\\n" % k)
for elem in env[k].split(os.pathsep):
log_fn("+ %s%s\\\n" % (shellquote(elem), os.pathsep))
else:
log_fn("+ %s=%s \\\n" % (k, shellquote(env[k])))
def check_cmd(
cmd: list[str],
env: Env | None = None,
cwd: str | None = None,
allow_fail: bool = False,
log_file: str | None = None,
) -> None:
"""Run the command and abort on failure"""
rc = run_cmd(cmd, env=env, cwd=cwd, allow_fail=allow_fail, log_file=log_file)
if rc != 0:
raise RuntimeError(f"Failure exit code {rc} for command {cmd}")
def run_cmd(
cmd: list[str],
env: Env | None = None,
cwd: str | None = None,
allow_fail: bool = False,
log_file: str | None = None,
preexec_fn: Callable[[], None] | None = None,
) -> int:
def log_to_stdout(msg: str) -> None:
sys.stdout.buffer.write(msg.encode(errors="surrogateescape"))
if log_file is not None:
with open(log_file, "a", encoding="utf-8", errors="surrogateescape") as log:
# pyre-fixme[53]: Captured variable `log` is not annotated.
def log_function(msg: str) -> None:
log.write(msg)
log_to_stdout(msg)
return _run_cmd(
cmd,
env=env,
cwd=cwd,
allow_fail=allow_fail,
log_fn=log_function,
preexec_fn=preexec_fn,
)
else:
return _run_cmd(
cmd,
env=env,
cwd=cwd,
allow_fail=allow_fail,
log_fn=log_to_stdout,
preexec_fn=preexec_fn,
)
def _run_cmd(
cmd: list[str],
env: Env | None,
cwd: str | None,
allow_fail: bool,
log_fn: Callable[[str], None],
preexec_fn: Callable[[], None] | None = None,
) -> int:
log_fn("---\n")
try:
cmd_str = " \\\n+ ".join(shellquote(arg) for arg in cmd)
except TypeError:
# eg: one of the elements is None
raise RunCommandError("problem quoting cmd: %r" % cmd)
if env:
assert isinstance(env, Env)
_print_env_diff(env, log_fn)
# Convert from our Env type to a regular dict.
# This is needed because python3 looks up b'PATH' and 'PATH'
# and emits an error if both are present. In our Env type
# we'll return the same value for both requests, but we don't
# have duplicate potentially conflicting values which is the
# spirit of the check.
env_dict: dict[str, str] | None = dict(env.items())
else:
env_dict = None
if cwd:
log_fn("+ cd %s && \\\n" % shellquote(cwd))
# Our long path escape sequence may confuse cmd.exe, so if the cwd
# is short enough, strip that off.
if is_windows() and (len(cwd) < 250) and cwd.startswith("\\\\?\\"):
cwd = cwd[4:]
log_fn("+ %s\n" % cmd_str)
isinteractive = os.isatty(sys.stdout.fileno())
if isinteractive:
stdout = None
sys.stdout.buffer.flush()
else:
stdout = subprocess.PIPE
try:
p = subprocess.Popen(
cmd,
env=env_dict,
cwd=cwd,
stdout=stdout,
stderr=subprocess.STDOUT,
preexec_fn=preexec_fn,
)
except (TypeError, ValueError, OSError) as exc:
log_fn("error running `%s`: %s" % (cmd_str, exc))
raise RunCommandError(
"%s while running `%s` with env=%r\nos.environ=%r"
% (str(exc), cmd_str, env_dict, os.environ)
)
if not isinteractive:
_pipe_output(p, log_fn)
p.wait()
if p.returncode != 0 and not allow_fail:
raise subprocess.CalledProcessError(p.returncode, cmd)
return p.returncode
if hasattr(select, "poll"):
def _pipe_output(p: subprocess.Popen[bytes], log_fn: Callable[[str], None]) -> None:
"""Read output from p.stdout and call log_fn() with each chunk of data as it
becomes available."""
# Perform non-blocking reads
import fcntl
assert p.stdout is not None
fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
poll = select.poll()
poll.register(p.stdout.fileno(), select.POLLIN)
buffer_size = 4096
while True:
poll.poll()
data = p.stdout.read(buffer_size)
if not data:
break
# log_fn() accepts arguments as str (binary in Python 2, unicode in
# Python 3). In Python 3 the subprocess output will be plain bytes,
# and need to be decoded.
if not isinstance(data, str):
data = data.decode("utf-8", errors="surrogateescape")
log_fn(data)
else:
def _pipe_output(p: subprocess.Popen[bytes], log_fn: Callable[[str], None]) -> None:
"""Read output from p.stdout and call log_fn() with each chunk of data as it
becomes available."""
# Perform blocking reads. Use a smaller buffer size to avoid blocking
# for very long when data is available.
assert p.stdout is not None
buffer_size = 64
while True:
# pyre-fixme[16]: Optional type has no attribute `read`.
data = p.stdout.read(buffer_size)
if not data:
break
# log_fn() accepts arguments as str (binary in Python 2, unicode in
# Python 3). In Python 3 the subprocess output will be plain bytes,
# and need to be decoded.
if not isinstance(data, str):
data = data.decode("utf-8", errors="surrogateescape")
log_fn(data)
================================================
FILE: build/fbcode_builder/getdeps/subcmd.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
from __future__ import annotations
import argparse
from collections.abc import Callable
class SubCmd:
NAME: str | None = None
HELP: str | None = None
def run(self, args: argparse.Namespace) -> int:
"""perform the command"""
return 0
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
# Subclasses should override setup_parser() if they have any
# command line options or arguments.
pass
CmdTable: list[type[SubCmd]] = []
def add_subcommands(
parser: argparse._SubParsersAction[argparse.ArgumentParser],
common_args: argparse.ArgumentParser,
cmd_table: list[type[SubCmd]] = CmdTable,
) -> None:
"""Register parsers for the defined commands with the provided parser"""
for cls in cmd_table:
command = cls()
command_parser = parser.add_parser(
# pyre-fixme[6]: For 1st argument expected `str` but got `Optional[str]`.
command.NAME,
help=command.HELP,
parents=[common_args],
)
command.setup_parser(command_parser)
command_parser.set_defaults(func=command.run)
def cmd(
name: str,
help: str | None = None,
cmd_table: list[type[SubCmd]] = CmdTable,
) -> Callable[[type[SubCmd]], type[SubCmd]]:
"""
@cmd() is a decorator that can be used to help define Subcmd instances
Example usage:
@subcmd('list', 'Show the result list')
class ListCmd(Subcmd):
def run(self, args):
# Perform the command actions here...
pass
"""
def wrapper(cls: type[SubCmd]) -> type[SubCmd]:
class SubclassedCmd(cls):
NAME = name
HELP = help
# pyre-fixme[6]: For 1st argument expected `Type[SubCmd]` but got
# `Type[SubclassedCmd]`.
# pyre-fixme[16]: Callable `cmd` has no attribute `wrapper`.
cmd_table.append(SubclassedCmd)
# pyre-fixme[7]: Expected `Type[SubCmd]` but got `Type[SubclassedCmd]`.
return SubclassedCmd
return wrapper
================================================
FILE: build/fbcode_builder/getdeps/test/expr_test.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
import unittest
from ..expr import parse_expr
class ExprTest(unittest.TestCase):
def test_equal(self) -> None:
valid_variables = {"foo", "some_var", "another_var"}
e = parse_expr("foo=bar", valid_variables)
self.assertTrue(e.eval({"foo": "bar"}))
self.assertFalse(e.eval({"foo": "not-bar"}))
self.assertFalse(e.eval({"not-foo": "bar"}))
def test_not_equal(self) -> None:
valid_variables = {"foo"}
e = parse_expr("not(foo=bar)", valid_variables)
self.assertFalse(e.eval({"foo": "bar"}))
self.assertTrue(e.eval({"foo": "not-bar"}))
def test_bad_not(self) -> None:
valid_variables = {"foo"}
with self.assertRaises(Exception):
parse_expr("foo=not(bar)", valid_variables)
def test_bad_variable(self) -> None:
valid_variables = {"bar"}
with self.assertRaises(Exception):
parse_expr("foo=bar", valid_variables)
def test_all(self) -> None:
valid_variables = {"foo", "baz"}
e = parse_expr("all(foo = bar, baz = qux)", valid_variables)
self.assertTrue(e.eval({"foo": "bar", "baz": "qux"}))
self.assertFalse(e.eval({"foo": "bar", "baz": "nope"}))
self.assertFalse(e.eval({"foo": "nope", "baz": "nope"}))
def test_any(self) -> None:
valid_variables = {"foo", "baz"}
e = parse_expr("any(foo = bar, baz = qux)", valid_variables)
self.assertTrue(e.eval({"foo": "bar", "baz": "qux"}))
self.assertTrue(e.eval({"foo": "bar", "baz": "nope"}))
self.assertFalse(e.eval({"foo": "nope", "baz": "nope"}))
================================================
FILE: build/fbcode_builder/getdeps/test/fixtures/duplicate/foo
================================================
[manifest]
name = foo
================================================
FILE: build/fbcode_builder/getdeps/test/fixtures/duplicate/subdir/foo
================================================
[manifest]
name = foo
================================================
FILE: build/fbcode_builder/getdeps/test/manifest_test.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
import sys
import unittest
from ..load import load_all_manifests, patch_loader
from ..manifest import ManifestParser
class ManifestTest(unittest.TestCase):
def test_missing_section(self) -> None:
with self.assertRaisesRegex(
Exception, "manifest file test is missing required section manifest"
):
ManifestParser("test", "")
def test_missing_name(self) -> None:
with self.assertRaisesRegex(
Exception,
"manifest file test section 'manifest' is missing required field 'name'",
):
ManifestParser(
"test",
"""
[manifest]
""",
)
def test_minimal(self) -> None:
p = ManifestParser(
"test",
"""
[manifest]
name = test
""",
)
self.assertEqual(p.name, "test")
self.assertEqual(p.fbsource_path, None)
def test_minimal_with_fbsource_path(self) -> None:
p = ManifestParser(
"test",
"""
[manifest]
name = test
fbsource_path = fbcode/wat
""",
)
self.assertEqual(p.name, "test")
self.assertEqual(p.fbsource_path, "fbcode/wat")
def test_unknown_field(self) -> None:
with self.assertRaisesRegex(
Exception,
(
"manifest file test section 'manifest' contains "
"unknown field 'invalid.field'"
),
):
ManifestParser(
"test",
"""
[manifest]
name = test
invalid.field = woot
""",
)
def test_invalid_section_name(self) -> None:
with self.assertRaisesRegex(
Exception, "manifest file test contains unknown section 'invalid.section'"
):
ManifestParser(
"test",
"""
[manifest]
name = test
[invalid.section]
foo = bar
""",
)
def test_value_in_dependencies_section(self) -> None:
with self.assertRaisesRegex(
Exception,
(
"manifest file test section 'dependencies' has "
"'foo = bar' but this section doesn't allow "
"specifying values for its entries"
),
):
ManifestParser(
"test",
"""
[manifest]
name = test
[dependencies]
foo = bar
""",
)
def test_invalid_conditional_section_name(self) -> None:
with self.assertRaisesRegex(
Exception,
(
"manifest file test section 'dependencies.=' "
"has invalid conditional: expected "
"identifier found ="
),
):
ManifestParser(
"test",
"""
[manifest]
name = test
[dependencies.=]
""",
)
def test_section_as_args(self) -> None:
p = ManifestParser(
"test",
"""
[manifest]
name = test
[dependencies]
a
b
c
[dependencies.test=on]
foo
""",
)
self.assertEqual(p.get_section_as_args("dependencies"), ["a", "b", "c"])
self.assertEqual(
p.get_section_as_args("dependencies", {"test": "off"}), ["a", "b", "c"]
)
self.assertEqual(
p.get_section_as_args("dependencies", {"test": "on"}),
["a", "b", "c", "foo"],
)
p2 = ManifestParser(
"test",
"""
[manifest]
name = test
[autoconf.args]
--prefix=/foo
--with-woot
""",
)
self.assertEqual(
p2.get_section_as_args("autoconf.args"), ["--prefix=/foo", "--with-woot"]
)
def test_section_as_dict(self) -> None:
p = ManifestParser(
"test",
"""
[manifest]
name = test
[cmake.defines]
foo = bar
[cmake.defines.test=on]
foo = baz
""",
)
self.assertEqual(p.get_section_as_dict("cmake.defines", {}), {"foo": "bar"})
self.assertEqual(
p.get_section_as_dict("cmake.defines", {"test": "on"}), {"foo": "baz"}
)
p2 = ManifestParser(
"test",
"""
[manifest]
name = test
[cmake.defines.test=on]
foo = baz
[cmake.defines]
foo = bar
""",
)
self.assertEqual(
p2.get_section_as_dict("cmake.defines", {"test": "on"}),
{"foo": "bar"},
msg="sections cascade in the order they appear in the manifest",
)
def test_parse_common_manifests(self) -> None:
patch_loader(__name__)
# pyre-fixme[6]: For 1st argument expected `BuildOptions` but got `None`.
manifests = load_all_manifests(None)
self.assertNotEqual(0, len(manifests), msg="parsed some number of manifests")
def test_mismatch_name(self) -> None:
with self.assertRaisesRegex(
Exception,
"filename of the manifest 'foo' does not match the manifest name 'bar'",
):
ManifestParser(
"foo",
"""
[manifest]
name = bar
""",
)
def test_duplicate_manifest(self) -> None:
patch_loader(__name__, "fixtures/duplicate")
with self.assertRaisesRegex(Exception, "found duplicate manifest 'foo'"):
# pyre-fixme[6]: For 1st argument expected `BuildOptions` but got `None`.
load_all_manifests(None)
if sys.version_info < (3, 2):
def assertRaisesRegex(self, *args, **kwargs):
return self.assertRaisesRegex(*args, **kwargs)
================================================
FILE: build/fbcode_builder/getdeps/test/platform_test.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
import unittest
from ..platform import HostType
class PlatformTest(unittest.TestCase):
def test_create(self) -> None:
p = HostType()
self.assertNotEqual(p.ostype, None, msg="probed and returned something")
tuple_string = p.as_tuple_string()
round_trip = HostType.from_tuple_string(tuple_string)
self.assertEqual(round_trip, p)
def test_rendering_of_none(self) -> None:
p = HostType(ostype="foo")
self.assertEqual(p.as_tuple_string(), "foo-none-none")
def test_is_methods(self) -> None:
p = HostType(ostype="windows")
self.assertTrue(p.is_windows())
self.assertFalse(p.is_darwin())
self.assertFalse(p.is_linux())
p = HostType(ostype="darwin")
self.assertFalse(p.is_windows())
self.assertTrue(p.is_darwin())
self.assertFalse(p.is_linux())
p = HostType(ostype="linux")
self.assertFalse(p.is_windows())
self.assertFalse(p.is_darwin())
self.assertTrue(p.is_linux())
================================================
FILE: build/fbcode_builder/getdeps/test/retry_test.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
import unittest
from unittest.mock import call, MagicMock, patch
from ..buildopts import BuildOptions
from ..errors import TransientFailure
from ..fetcher import ArchiveFetcher
from ..manifest import ManifestParser
class RetryTest(unittest.TestCase):
def _get_build_opts(self) -> BuildOptions:
mock_build_opts = MagicMock(spec=BuildOptions)
mock_build_opts.scratch_dir = "/path/to/scratch_dir"
return mock_build_opts
def _get_manifest(self) -> ManifestParser:
mock_manifest_parser = MagicMock(spec=ManifestParser)
mock_manifest_parser.name = "mock_manifest_parser"
return mock_manifest_parser
def _get_archive_fetcher(self) -> ArchiveFetcher:
return ArchiveFetcher(
build_options=self._get_build_opts(),
manifest=self._get_manifest(),
url="https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz",
sha256="896d76ff65c88f5fd9e42f90d152b0579049158a163431dd77cdc57748b1d7b0",
)
@patch("os.makedirs")
@patch("os.environ.get")
@patch("time.sleep")
@patch("subprocess.run")
def test_no_retries(
self, mock_run, mock_sleep, mock_os_environ_get, mock_makedirs
) -> None:
def custom_makedirs(path, exist_ok=False):
return None
def custom_get(key, default=None):
if key == "GETDEPS_USE_WGET":
return "1"
elif key == "GETDEPS_WGET_ARGS":
return ""
else:
return None
mock_makedirs.side_effect = custom_makedirs
mock_os_environ_get.side_effect = custom_get
mock_sleep.side_effect = None
fetcher = self._get_archive_fetcher()
fetcher._verify_hash = MagicMock(return_value=None)
fetcher._download()
mock_sleep.assert_has_calls([], any_order=False)
mock_run.assert_called_once_with(
[
"wget",
"-O",
"/path/to/scratch_dir/downloads/mock_manifest_parser-v256.7.tar.gz",
"https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz",
],
capture_output=True,
)
@patch("random.random")
@patch("os.makedirs")
@patch("os.environ.get")
@patch("time.sleep")
@patch("subprocess.run")
def test_retries(
self, mock_run, mock_sleep, mock_os_environ_get, mock_makedirs, mock_random
) -> None:
def custom_makedirs(path, exist_ok=False):
return None
def custom_get(key, default=None):
if key == "GETDEPS_USE_WGET":
return "1"
elif key == "GETDEPS_WGET_ARGS":
return ""
else:
return None
mock_random.return_value = 0
mock_run.side_effect = [
IOError(""),
IOError(""),
None,
]
mock_makedirs.side_effect = custom_makedirs
mock_os_environ_get.side_effect = custom_get
mock_sleep.side_effect = None
fetcher = self._get_archive_fetcher()
fetcher._verify_hash = MagicMock(return_value=None)
fetcher._download()
mock_sleep.assert_has_calls([call(2), call(4)], any_order=False)
calls = [
call(
[
"wget",
"-O",
"/path/to/scratch_dir/downloads/mock_manifest_parser-v256.7.tar.gz",
"https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz",
],
capture_output=True,
),
] * 3
mock_run.assert_has_calls(calls, any_order=False)
@patch("random.random")
@patch("os.makedirs")
@patch("os.environ.get")
@patch("time.sleep")
@patch("subprocess.run")
def test_all_retries(
self, mock_run, mock_sleep, mock_os_environ_get, mock_makedirs, mock_random
) -> None:
def custom_makedirs(path, exist_ok=False):
return None
def custom_get(key, default=None):
if key == "GETDEPS_USE_WGET":
return "1"
elif key == "GETDEPS_WGET_ARGS":
return ""
else:
return None
mock_random.return_value = 0
mock_run.side_effect = IOError(
""
)
mock_makedirs.side_effect = custom_makedirs
mock_os_environ_get.side_effect = custom_get
mock_sleep.side_effect = None
fetcher = self._get_archive_fetcher()
fetcher._verify_hash = MagicMock(return_value=None)
with self.assertRaises(TransientFailure):
fetcher._download()
mock_sleep.assert_has_calls(
[call(2), call(4), call(8), call(10)], any_order=False
)
calls = [
call(
[
"wget",
"-O",
"/path/to/scratch_dir/downloads/mock_manifest_parser-v256.7.tar.gz",
"https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz",
],
capture_output=True,
),
] * 5
mock_run.assert_has_calls(calls, any_order=False)
================================================
FILE: build/fbcode_builder/getdeps/test/scratch_test.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
import unittest
from ..buildopts import find_existing_win32_subst_for_path
class Win32SubstTest(unittest.TestCase):
def test_no_existing_subst(self) -> None:
self.assertIsNone(
find_existing_win32_subst_for_path(
r"C:\users\alice\appdata\local\temp\fbcode_builder_getdeps",
subst_mapping={},
)
)
self.assertIsNone(
find_existing_win32_subst_for_path(
r"C:\users\alice\appdata\local\temp\fbcode_builder_getdeps",
subst_mapping={"X:\\": r"C:\users\alice\appdata\local\temp\other"},
)
)
def test_exact_match_returns_drive_path(self) -> None:
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:\temp\fbcode_builder_getdeps",
subst_mapping={"X:\\": r"C:\temp\fbcode_builder_getdeps"},
),
"X:\\",
)
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:/temp/fbcode_builder_getdeps",
subst_mapping={"X:\\": r"C:/temp/fbcode_builder_getdeps"},
),
"X:\\",
)
def test_multiple_exact_matches_returns_arbitrary_drive_path(self) -> None:
self.assertIn(
find_existing_win32_subst_for_path(
r"C:\temp\fbcode_builder_getdeps",
subst_mapping={
"X:\\": r"C:\temp\fbcode_builder_getdeps",
"Y:\\": r"C:\temp\fbcode_builder_getdeps",
"Z:\\": r"C:\temp\fbcode_builder_getdeps",
},
),
("X:\\", "Y:\\", "Z:\\"),
)
def test_drive_letter_is_case_insensitive(self) -> None:
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:\temp\fbcode_builder_getdeps",
subst_mapping={"X:\\": r"c:\temp\fbcode_builder_getdeps"},
),
"X:\\",
)
def test_path_components_are_case_insensitive(self) -> None:
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:\TEMP\FBCODE_builder_getdeps",
subst_mapping={"X:\\": r"C:\temp\fbcode_builder_getdeps"},
),
"X:\\",
)
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:\temp\fbcode_builder_getdeps",
subst_mapping={"X:\\": r"C:\TEMP\FBCODE_builder_getdeps"},
),
"X:\\",
)
================================================
FILE: build/fbcode_builder/getdeps/test/strip_marker_test.py
================================================
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-strict
import os
import tempfile
import unittest
from ..fetcher import filter_strip_marker
from ..manifest import ManifestParser
class ManifestStripMarkerTest(unittest.TestCase):
def test_default_strip_marker(self) -> None:
p = ManifestParser(
"test",
"""
[manifest]
name = test
""",
)
self.assertEqual(p.shipit_strip_marker, "@fb-only")
def test_custom_strip_marker(self) -> None:
p = ManifestParser(
"test",
"""
[manifest]
name = test
shipit_strip_marker = @oss-disable
""",
)
self.assertEqual(p.shipit_strip_marker, "@oss-disable")
class FilterStripMarkerTest(unittest.TestCase):
def _write_temp(self, content: str) -> str:
fd, path = tempfile.mkstemp(suffix=".txt")
os.close(fd)
with open(path, "w") as f:
f.write(content)
return path
def _read(self, path: str) -> str:
with open(path, "r") as f:
return f.read()
def test_single_line_removal(self) -> None:
path = self._write_temp("keep this\nremove this @fb-only\nkeep this too\n")
try:
filter_strip_marker(path, "@fb-only")
self.assertEqual(self._read(path), "keep this\nkeep this too\n")
finally:
os.unlink(path)
def test_block_removal(self) -> None:
content = (
"before\n"
"// @fb-only-start\n"
"secret stuff\n"
"more secret\n"
"// @fb-only-end\n"
"after\n"
)
path = self._write_temp(content)
try:
filter_strip_marker(path, "@fb-only")
self.assertEqual(self._read(path), "before\nafter\n")
finally:
os.unlink(path)
def test_no_marker_present_no_change(self) -> None:
original = "nothing special here\njust plain code\n"
path = self._write_temp(original)
try:
filter_strip_marker(path, "@fb-only")
self.assertEqual(self._read(path), original)
finally:
os.unlink(path)
def test_custom_marker_single_line(self) -> None:
content = "keep\nremove @oss-disable\nkeep too\n"
path = self._write_temp(content)
try:
filter_strip_marker(path, "@oss-disable")
self.assertEqual(self._read(path), "keep\nkeep too\n")
finally:
os.unlink(path)
def test_custom_marker_block(self) -> None:
content = (
"before\n"
"# @oss-disable-start\n"
"internal only\n"
"# @oss-disable-end\n"
"after\n"
)
path = self._write_temp(content)
try:
filter_strip_marker(path, "@oss-disable")
self.assertEqual(self._read(path), "before\nafter\n")
finally:
os.unlink(path)
def test_custom_marker_ignores_default(self) -> None:
"""When using a custom marker, @fb-only lines should be kept."""
content = "keep @fb-only\nremove @oss-disable\nplain\n"
path = self._write_temp(content)
try:
filter_strip_marker(path, "@oss-disable")
self.assertEqual(self._read(path), "keep @fb-only\nplain\n")
finally:
os.unlink(path)
def test_mixed_single_and_block(self) -> None:
content = (
"line1\n"
"line2 @fb-only\n"
"line3\n"
"// @fb-only-start\n"
"block content\n"
"// @fb-only-end\n"
"line4\n"
)
path = self._write_temp(content)
try:
filter_strip_marker(path, "@fb-only")
self.assertEqual(self._read(path), "line1\nline3\nline4\n")
finally:
os.unlink(path)
def test_marker_with_regex_metacharacters(self) -> None:
"""Markers containing regex metacharacters should be escaped properly."""
content = "keep\nremove @fb.only\nkeep too\n"
path = self._write_temp(content)
try:
# With proper escaping, the dot is literal, not a wildcard
filter_strip_marker(path, "@fb.only")
self.assertEqual(self._read(path), "keep\nkeep too\n")
finally:
os.unlink(path)
def test_binary_file_skipped(self) -> None:
"""Binary files that can't be decoded as UTF-8 should be skipped."""
fd, path = tempfile.mkstemp(suffix=".bin")
os.close(fd)
binary_content = b"\x80\x81\x82\xff\xfe"
with open(path, "wb") as f:
f.write(binary_content)
try:
filter_strip_marker(path, "@fb-only")
with open(path, "rb") as f:
self.assertEqual(f.read(), binary_content)
finally:
os.unlink(path)
================================================
FILE: build/fbcode_builder/getdeps.py
================================================
#!/usr/bin/env python3
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
import argparse
import json
import os
import shutil
import subprocess
import sys
import tarfile
import tempfile
# We don't import cache.create_cache directly as the facebook
# specific import below may monkey patch it, and we want to
# observe the patched version of this function!
import getdeps.cache as cache_module
from getdeps.buildopts import setup_build_options
from getdeps.dyndeps import create_dyn_dep_munger
from getdeps.errors import TransientFailure
from getdeps.fetcher import (
file_name_is_cmake_file,
is_public_commit,
list_files_under_dir_newer_than_timestamp,
SystemPackageFetcher,
)
from getdeps.load import ManifestLoader
from getdeps.manifest import ManifestParser
from getdeps.platform import HostType
from getdeps.runcmd import check_cmd
from getdeps.subcmd import add_subcommands, cmd, SubCmd
try:
import getdeps.facebook # noqa: F401
except ImportError:
# we don't ship the facebook specific subdir,
# so allow that to fail silently
pass
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "getdeps"))
class UsageError(Exception):
pass
# Shared argument definition for --build-type used by multiple commands
BUILD_TYPE_ARG = {
"help": "Set the build type explicitly: Debug (unoptimized, debug symbols), RelWithDebInfo (optimized with debug symbols, default), MinSizeRel (size-optimized, no debug), or Release (optimized, no debug).",
"choices": ["Debug", "Release", "RelWithDebInfo", "MinSizeRel"],
"action": "store",
"default": "RelWithDebInfo",
}
@cmd("validate-manifest", "parse a manifest and validate that it is correct")
class ValidateManifest(SubCmd):
def run(self, args):
try:
ManifestParser(file_name=args.file_name)
print("OK", file=sys.stderr)
return 0
except Exception as exc:
print("ERROR: %s" % str(exc), file=sys.stderr)
return 1
def setup_parser(self, parser):
parser.add_argument("file_name", help="path to the manifest file")
@cmd("show-host-type", "outputs the host type tuple for the host machine")
class ShowHostType(SubCmd):
def run(self, args):
host = HostType()
print("%s" % host.as_tuple_string())
return 0
class ProjectCmdBase(SubCmd):
def run(self, args):
opts = setup_build_options(args)
if args.current_project is not None:
opts.repo_project = args.current_project
if args.project is None:
if opts.repo_project is None:
raise UsageError(
"no project name specified, and no .projectid file found"
)
if opts.repo_project == "fbsource":
# The fbsource repository is a little special. There is no project
# manifest file for it. A specific project must always be explicitly
# specified when building from fbsource.
raise UsageError(
"no project name specified (required when building in fbsource)"
)
args.project = opts.repo_project
ctx_gen = opts.get_context_generator()
if args.test_dependencies:
ctx_gen.set_value_for_all_projects("test", "on")
if args.enable_tests:
ctx_gen.set_value_for_project(args.project, "test", "on")
else:
ctx_gen.set_value_for_project(args.project, "test", "off")
if opts.shared_libs:
ctx_gen.set_value_for_all_projects("shared_libs", "on")
loader = ManifestLoader(opts, ctx_gen)
self.process_project_dir_arguments(args, loader)
manifest = loader.load_manifest(args.project)
return self.run_project_cmd(args, loader, manifest)
def process_project_dir_arguments(self, args, loader):
def parse_project_arg(arg, arg_type):
parts = arg.split(":")
if len(parts) == 2:
project, path = parts
elif len(parts) == 1:
project = args.project
path = parts[0]
# On Windows path contains colon, e.g. C:\open
elif os.name == "nt" and len(parts) == 3:
project = parts[0]
path = parts[1] + ":" + parts[2]
else:
raise UsageError(
"invalid %s argument; too many ':' characters: %s" % (arg_type, arg)
)
return project, os.path.abspath(path)
# If we are currently running from a project repository,
# use the current repository for the project sources.
build_opts = loader.build_opts
if build_opts.repo_project is not None and build_opts.repo_root is not None:
loader.set_project_src_dir(build_opts.repo_project, build_opts.repo_root)
for arg in args.src_dir:
project, path = parse_project_arg(arg, "--src-dir")
loader.set_project_src_dir(project, path)
for arg in args.build_dir:
project, path = parse_project_arg(arg, "--build-dir")
loader.set_project_build_dir(project, path)
for arg in args.install_dir:
project, path = parse_project_arg(arg, "--install-dir")
loader.set_project_install_dir(project, path)
for arg in args.project_install_prefix:
project, path = parse_project_arg(arg, "--install-prefix")
loader.set_project_install_prefix(project, path)
def setup_parser(self, parser):
parser.add_argument(
"project",
nargs="?",
help=(
"name of the project or path to a manifest "
"file describing the project"
),
)
parser.add_argument(
"--no-tests",
action="store_false",
dest="enable_tests",
default=True,
help="Disable building tests for this project.",
)
parser.add_argument(
"--test-dependencies",
action="store_true",
help="Enable building tests for dependencies as well.",
)
parser.add_argument(
"--current-project",
help="Specify the name of the fbcode_builder manifest file for the "
"current repository. If not specified, the code will attempt to find "
"this in a .projectid file in the repository root.",
)
parser.add_argument(
"--src-dir",
default=[],
action="append",
help="Specify a local directory to use for the project source, "
"rather than fetching it.",
)
parser.add_argument(
"--build-dir",
default=[],
action="append",
help="Explicitly specify the build directory to use for the "
"project, instead of the default location in the scratch path. "
"This only affects the project specified, and not its dependencies.",
)
parser.add_argument(
"--install-dir",
default=[],
action="append",
help="Explicitly specify the install directory to use for the "
"project, instead of the default location in the scratch path. "
"This only affects the project specified, and not its dependencies.",
)
parser.add_argument(
"--project-install-prefix",
default=[],
action="append",
help="Specify the final deployment installation path for a project",
)
self.setup_project_cmd_parser(parser)
def setup_project_cmd_parser(self, parser):
pass
def create_builder(self, loader, manifest):
fetcher = loader.create_fetcher(manifest)
src_dir = fetcher.get_src_dir()
ctx = loader.ctx_gen.get_context(manifest.name)
build_dir = loader.get_project_build_dir(manifest)
inst_dir = loader.get_project_install_dir(manifest)
return manifest.create_builder(
loader.build_opts,
src_dir,
build_dir,
inst_dir,
ctx,
loader,
loader.dependencies_of(manifest),
)
def check_built(self, loader, manifest):
built_marker = os.path.join(
loader.get_project_install_dir(manifest), ".built-by-getdeps"
)
return os.path.exists(built_marker)
class CachedProject:
"""A helper that allows calling the cache logic for a project
from both the build and the fetch code"""
def __init__(self, cache, loader, m):
self.m = m
self.inst_dir = loader.get_project_install_dir(m)
self.project_hash = loader.get_project_hash(m)
self.ctx = loader.ctx_gen.get_context(m.name)
self.loader = loader
self.cache = cache
self.cache_key = "-".join(
(
m.name,
self.ctx.get("os"),
self.ctx.get("distro") or "none",
self.ctx.get("distro_vers") or "none",
self.project_hash,
)
)
self.cache_file_name = self.cache_key + "-buildcache.tgz"
def is_cacheable(self):
"""We only cache third party projects"""
return self.cache and self.m.shipit_project is None
def was_cached(self):
cached_marker = os.path.join(self.inst_dir, ".getdeps-cached-build")
return os.path.exists(cached_marker)
def download(self):
if self.is_cacheable() and not os.path.exists(self.inst_dir):
print("check cache for %s" % self.cache_file_name)
dl_dir = os.path.join(self.loader.build_opts.scratch_dir, "downloads")
if not os.path.exists(dl_dir):
os.makedirs(dl_dir)
try:
target_file_name = os.path.join(dl_dir, self.cache_file_name)
if self.cache.download_to_file(self.cache_file_name, target_file_name):
tf = tarfile.open(target_file_name, "r")
print(
"Extracting %s -> %s..." % (self.cache_file_name, self.inst_dir)
)
tf.extractall(self.inst_dir)
cached_marker = os.path.join(self.inst_dir, ".getdeps-cached-build")
with open(cached_marker, "w") as f:
f.write("\n")
return True
except Exception as exc:
print("%s" % str(exc))
return False
def upload(self):
if self.is_cacheable():
# We can prepare an archive and stick it in LFS
tempdir = tempfile.mkdtemp()
tarfilename = os.path.join(tempdir, self.cache_file_name)
print("Archiving for cache: %s..." % tarfilename)
tf = tarfile.open(tarfilename, "w:gz")
tf.add(self.inst_dir, arcname=".")
tf.close()
try:
self.cache.upload_from_file(self.cache_file_name, tarfilename)
except Exception as exc:
print(
"Failed to upload to cache (%s), continue anyway" % str(exc),
file=sys.stderr,
)
shutil.rmtree(tempdir)
@cmd("fetch", "fetch the code for a given project")
class FetchCmd(ProjectCmdBase):
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--recursive",
help="fetch the transitive deps also",
action="store_true",
default=False,
)
parser.add_argument(
"--host-type",
help=(
"When recursively fetching, fetch deps for "
"this host type rather than the current system"
),
)
def run_project_cmd(self, args, loader, manifest):
if args.recursive:
projects = loader.manifests_in_dependency_order()
else:
projects = [manifest]
cache = cache_module.create_cache()
for m in projects:
fetcher = loader.create_fetcher(m)
if isinstance(fetcher, SystemPackageFetcher):
# We are guaranteed that if the fetcher is set to
# SystemPackageFetcher then this item is completely
# satisfied by the appropriate system packages
continue
cached_project = CachedProject(cache, loader, m)
if cached_project.download():
continue
inst_dir = loader.get_project_install_dir(m)
built_marker = os.path.join(inst_dir, ".built-by-getdeps")
if os.path.exists(built_marker):
with open(built_marker, "r") as f:
built_hash = f.read().strip()
project_hash = loader.get_project_hash(m)
if built_hash == project_hash:
continue
# We need to fetch the sources
fetcher.update()
@cmd("install-system-deps", "Install system packages to satisfy the deps for a project")
class InstallSysDepsCmd(ProjectCmdBase):
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--recursive",
help="install the transitive deps also",
action="store_true",
default=False,
)
parser.add_argument(
"--dry-run",
action="store_true",
default=False,
help="Don't install, just print the commands specs we would run",
)
parser.add_argument(
"--os-type",
help="Filter to just this OS type to run",
choices=["linux", "darwin", "windows", "pacman-package"],
action="store",
dest="ostype",
default=None,
)
parser.add_argument(
"--distro",
help="Filter to just this distro to run",
choices=["ubuntu", "centos_stream"],
action="store",
dest="distro",
default=None,
)
parser.add_argument(
"--distro-version",
help="Filter to just this distro version",
action="store",
dest="distrovers",
default=None,
)
def run_project_cmd(self, args, loader, manifest):
if args.recursive:
projects = loader.manifests_in_dependency_order()
else:
projects = [manifest]
rebuild_ctx_gen = False
if args.ostype:
loader.build_opts.host_type.ostype = args.ostype
loader.build_opts.host_type.distro = None
loader.build_opts.host_type.distrovers = None
rebuild_ctx_gen = True
if args.distro:
loader.build_opts.host_type.distro = args.distro
loader.build_opts.host_type.distrovers = None
rebuild_ctx_gen = True
if args.distrovers:
loader.build_opts.host_type.distrovers = args.distrovers
rebuild_ctx_gen = True
if rebuild_ctx_gen:
loader.ctx_gen = loader.build_opts.get_context_generator()
manager = loader.build_opts.host_type.get_package_manager()
all_packages = {}
for m in projects:
ctx = loader.ctx_gen.get_context(m.name)
packages = m.get_required_system_packages(ctx)
for k, v in packages.items():
merged = all_packages.get(k, [])
merged += v
all_packages[k] = merged
cmd_argss = []
if manager == "rpm":
packages = sorted(set(all_packages["rpm"]))
if packages:
cmd_argss.append(
["sudo", "dnf", "install", "-y", "--skip-broken"] + packages
)
elif manager == "deb":
packages = sorted(set(all_packages["deb"]))
if packages:
cmd_argss.append(
[
"sudo",
"--preserve-env=http_proxy",
"apt-get",
"install",
"-y",
]
+ packages
)
cmd_argss.append(["pip", "install", "pex"])
elif manager == "homebrew":
packages = sorted(set(all_packages["homebrew"]))
if packages:
cmd_argss.append(["brew", "install"] + packages)
elif manager == "pacman-package":
packages = sorted(list(set(all_packages["pacman-package"])))
if packages:
cmd_argss.append(["pacman", "-S"] + packages)
else:
host_tuple = loader.build_opts.host_type.as_tuple_string()
print(
f"I don't know how to install any packages on this system {host_tuple}"
)
return
for cmd_args in cmd_argss:
if args.dry_run:
print(" ".join(cmd_args))
else:
check_cmd(cmd_args)
else:
print("no packages to install")
@cmd("list-deps", "lists the transitive deps for a given project")
class ListDepsCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
for m in loader.manifests_in_dependency_order():
print(m.name)
return 0
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--host-type",
help=(
"Produce the list for the specified host type, "
"rather than that of the current system"
),
)
def clean_dirs(opts):
for d in ["build", "installed", "extracted", "shipit"]:
d = os.path.join(opts.scratch_dir, d)
print("Cleaning %s..." % d)
if os.path.exists(d):
shutil.rmtree(d)
@cmd("clean", "clean up the scratch dir")
class CleanCmd(SubCmd):
def run(self, args):
opts = setup_build_options(args)
clean_dirs(opts)
@cmd("show-scratch-dir", "show the scratch dir")
class ShowScratchDirCmd(SubCmd):
def run(self, args):
opts = setup_build_options(args)
print(opts.scratch_dir)
@cmd("show-build-dir", "print the build dir for a given project")
class ShowBuildDirCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
if args.recursive:
manifests = loader.manifests_in_dependency_order()
else:
manifests = [manifest]
for m in manifests:
inst_dir = loader.get_project_build_dir(m)
print(inst_dir)
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--recursive",
help="print the transitive deps also",
action="store_true",
default=False,
)
@cmd("show-inst-dir", "print the installation dir for a given project")
class ShowInstDirCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
if args.recursive:
manifests = loader.manifests_in_dependency_order()
else:
manifests = [manifest]
for m in manifests:
fetcher = loader.create_fetcher(m)
if isinstance(fetcher, SystemPackageFetcher):
# We are guaranteed that if the fetcher is set to
# SystemPackageFetcher then this item is completely
# satisfied by the appropriate system packages
continue
inst_dir = loader.get_project_install_dir_respecting_install_prefix(m)
print(inst_dir)
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--recursive",
help="print the transitive deps also",
action="store_true",
default=False,
)
@cmd("query-paths", "print the paths for tooling to use")
class QueryPathsCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
if args.recursive:
manifests = loader.manifests_in_dependency_order()
else:
manifests = [manifest]
cache = cache_module.create_cache()
for m in manifests:
fetcher = loader.create_fetcher(m)
if isinstance(fetcher, SystemPackageFetcher):
# We are guaranteed that if the fetcher is set to
# SystemPackageFetcher then this item is completely
# satisfied by the appropriate system packages
continue
src_dir = fetcher.get_src_dir()
print(f"{m.name}_SOURCE={src_dir}")
inst_dir = loader.get_project_install_dir_respecting_install_prefix(m)
print(f"{m.name}_INSTALL={inst_dir}")
cached_project = CachedProject(cache, loader, m)
print(f"{m.name}_CACHE_KEY={cached_project.cache_key}")
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--recursive",
help="print the transitive deps also",
action="store_true",
default=False,
)
@cmd("show-source-dir", "print the source dir for a given project")
class ShowSourceDirCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
if args.recursive:
manifests = loader.manifests_in_dependency_order()
else:
manifests = [manifest]
for m in manifests:
fetcher = loader.create_fetcher(m)
print(fetcher.get_src_dir())
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--recursive",
help="print the transitive deps also",
action="store_true",
default=False,
)
@cmd("build", "build a given project")
class BuildCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
if args.clean:
clean_dirs(loader.build_opts)
print("Building on %s" % loader.ctx_gen.get_context(args.project))
projects = loader.manifests_in_dependency_order()
cache = cache_module.create_cache() if args.use_build_cache else None
dep_manifests = []
for m in projects:
dep_manifests.append(m)
fetcher = loader.create_fetcher(m)
if args.build_skip_lfs_download and hasattr(fetcher, "skip_lfs_download"):
print("skipping lfs download for %s" % m.name)
fetcher.skip_lfs_download()
if isinstance(fetcher, SystemPackageFetcher):
# We are guaranteed that if the fetcher is set to
# SystemPackageFetcher then this item is completely
# satisfied by the appropriate system packages
continue
if args.clean:
fetcher.clean()
build_dir = loader.get_project_build_dir(m)
inst_dir = loader.get_project_install_dir(m)
if (
m == manifest
and not args.only_deps
or m != manifest
and not args.no_deps
):
print("Assessing %s..." % m.name)
project_hash = loader.get_project_hash(m)
ctx = loader.ctx_gen.get_context(m.name)
built_marker = os.path.join(inst_dir, ".built-by-getdeps")
cached_project = CachedProject(cache, loader, m)
reconfigure, sources_changed = self.compute_source_change_status(
cached_project, fetcher, m, built_marker, project_hash
)
if os.path.exists(built_marker) and not cached_project.was_cached():
# We've previously built this. We may need to reconfigure if
# our deps have changed, so let's check them.
dep_reconfigure, dep_build = self.compute_dep_change_status(
m, built_marker, loader
)
if dep_reconfigure:
reconfigure = True
if dep_build:
sources_changed = True
extra_cmake_defines = (
json.loads(args.extra_cmake_defines)
if args.extra_cmake_defines
else {}
)
extra_b2_args = args.extra_b2_args or []
cmake_targets = args.cmake_target or ["install"]
if sources_changed or reconfigure or not os.path.exists(built_marker):
if os.path.exists(built_marker):
os.unlink(built_marker)
src_dir = fetcher.get_src_dir()
# Prepare builders write out config before the main builder runs
prepare_builders = m.create_prepare_builders(
loader.build_opts,
ctx,
src_dir,
build_dir,
inst_dir,
loader,
dep_manifests,
)
for preparer in prepare_builders:
preparer.prepare(reconfigure=reconfigure)
builder = m.create_builder(
loader.build_opts,
src_dir,
build_dir,
inst_dir,
ctx,
loader,
dep_manifests,
final_install_prefix=loader.get_project_install_prefix(m),
extra_cmake_defines=extra_cmake_defines,
cmake_targets=(cmake_targets if m == manifest else ["install"]),
extra_b2_args=extra_b2_args,
)
builder.build(reconfigure=reconfigure)
# If we are building the project (not dependency) and a specific
# cmake_target (not 'install') has been requested, then we don't
# set the built_marker. This allows subsequent runs of getdeps.py
# for the project to run with different cmake_targets to trigger
# cmake
has_built_marker = False
if not (m == manifest and "install" not in cmake_targets):
os.makedirs(os.path.dirname(built_marker), exist_ok=True)
with open(built_marker, "w") as f:
f.write(project_hash)
has_built_marker = True
# Only populate the cache from continuous build runs, and
# only if we have a built_marker.
if not args.skip_upload and has_built_marker:
if args.schedule_type == "continuous":
cached_project.upload()
elif args.schedule_type == "base_retry":
# Check if on public commit before uploading
if is_public_commit(loader.build_opts):
cached_project.upload()
elif args.verbose:
print("found good %s" % built_marker)
def compute_dep_change_status(self, m, built_marker, loader):
reconfigure = False
sources_changed = False
st = os.lstat(built_marker)
ctx = loader.ctx_gen.get_context(m.name)
dep_list = m.get_dependencies(ctx)
for dep in dep_list:
if reconfigure and sources_changed:
break
dep_manifest = loader.load_manifest(dep)
dep_root = loader.get_project_install_dir(dep_manifest)
for dep_file in list_files_under_dir_newer_than_timestamp(
dep_root, st.st_mtime
):
if os.path.basename(dep_file) == ".built-by-getdeps":
continue
if file_name_is_cmake_file(dep_file):
if not reconfigure:
reconfigure = True
print(
f"Will reconfigure cmake because {dep_file} is newer than {built_marker}"
)
else:
if not sources_changed:
sources_changed = True
print(
f"Will run build because {dep_file} is newer than {built_marker}"
)
if reconfigure and sources_changed:
break
return reconfigure, sources_changed
def compute_source_change_status(
self, cached_project, fetcher, m, built_marker, project_hash
):
reconfigure = False
sources_changed = False
if cached_project.download():
if not os.path.exists(built_marker):
fetcher.update()
else:
check_fetcher = True
if os.path.exists(built_marker):
check_fetcher = False
with open(built_marker, "r") as f:
built_hash = f.read().strip()
if built_hash == project_hash:
if cached_project.is_cacheable():
# We can blindly trust the build status
reconfigure = False
sources_changed = False
else:
# Otherwise, we may have changed the source, so let's
# check in with the fetcher layer
check_fetcher = True
else:
# Some kind of inconsistency with a prior build,
# let's run it again to be sure
os.unlink(built_marker)
reconfigure = True
sources_changed = True
# While we don't need to consult the fetcher for the
# status in this case, we may still need to have eg: shipit
# run in order to have a correct source tree.
fetcher.update()
if check_fetcher:
change_status = fetcher.update()
reconfigure = change_status.build_changed()
sources_changed = change_status.sources_changed()
return reconfigure, sources_changed
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--clean",
action="store_true",
default=False,
help=(
"Clean up the build and installation area prior to building, "
"causing the projects to be built from scratch"
),
)
parser.add_argument(
"--no-deps",
action="store_true",
default=False,
help=(
"Only build the named project, not its deps. "
"This is most useful after you've built all of the deps, "
"and helps to avoid waiting for relatively "
"slow up-to-date-ness checks"
),
)
parser.add_argument(
"--only-deps",
action="store_true",
default=False,
help=(
"Only build the named project's deps. "
"This is most useful when you want to separate out building "
"of all of the deps and your project"
),
)
parser.add_argument(
"--no-build-cache",
action="store_false",
default=True,
dest="use_build_cache",
help="Do not attempt to use the build cache.",
)
parser.add_argument(
"--cmake-target",
help=("Repeatable argument that specifies targets for cmake build."),
default=[],
action="append",
)
parser.add_argument(
"--extra-b2-args",
help=(
"Repeatable argument that contains extra arguments to pass "
"to b2, which compiles boost. "
"e.g.: 'cxxflags=-fPIC' 'cflags=-fPIC'"
),
action="append",
)
parser.add_argument(
"--free-up-disk",
help="Remove unused tools and clean up intermediate files if possible to maximise space for the build",
action="store_true",
default=False,
)
parser.add_argument("--build-type", **BUILD_TYPE_ARG)
@cmd("fixup-dyn-deps", "Adjusts dynamic dependencies for packaging purposes")
class FixupDeps(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
projects = loader.manifests_in_dependency_order()
# Accumulate the install directories so that the build steps
# can find their dep installation
install_dirs = []
dep_manifests = []
for m in projects:
inst_dir = loader.get_project_install_dir_respecting_install_prefix(m)
install_dirs.append(inst_dir)
dep_manifests.append(m)
if m == manifest:
ctx = loader.ctx_gen.get_context(m.name)
env = loader.build_opts.compute_env_for_install_dirs(
loader, dep_manifests, ctx
)
dep_munger = create_dyn_dep_munger(
loader.build_opts, env, install_dirs, args.strip
)
if dep_munger is None:
print(f"dynamic dependency fixups not supported on {sys.platform}")
else:
dep_munger.process_deps(args.destdir, args.final_install_prefix)
def setup_project_cmd_parser(self, parser):
parser.add_argument("destdir", help="Where to copy the fixed up executables")
parser.add_argument(
"--final-install-prefix", help="specify the final installation prefix"
)
parser.add_argument(
"--strip",
action="store_true",
default=False,
help="Strip debug info while processing executables",
)
@cmd("test", "test a given project")
class TestCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
if not self.check_built(loader, manifest):
print("project %s has not been built" % manifest.name)
return 1
return self.create_builder(loader, manifest).run_tests(
schedule_type=args.schedule_type,
owner=args.test_owner,
test_filter=args.filter,
test_exclude=args.exclude,
retry=args.retry,
no_testpilot=args.no_testpilot,
timeout=args.timeout,
)
def setup_project_cmd_parser(self, parser):
parser.add_argument("--test-owner", help="Owner for testpilot")
parser.add_argument("--filter", help="Only run the tests matching the regex")
parser.add_argument("--exclude", help="Exclude tests matching the regex")
parser.add_argument(
"--retry",
type=int,
default=3,
help="Number of immediate retries for failed tests "
"(noop in continuous and testwarden runs)",
)
parser.add_argument(
"--no-testpilot",
help="Do not use Test Pilot even when available",
action="store_true",
)
parser.add_argument(
"--timeout",
type=int,
default=None,
help="Timeout in seconds for each individual test",
)
parser.add_argument("--build-type", **BUILD_TYPE_ARG)
@cmd(
"debug",
"start a shell in the given project's build dir with the correct environment for running the build",
)
class DebugCmd(ProjectCmdBase):
def run_project_cmd(self, args, loader, manifest):
self.create_builder(loader, manifest).debug(reconfigure=False)
@cmd(
"env",
"print the environment in a shell sourceable format",
)
class EnvCmd(ProjectCmdBase):
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--os-type",
help="Filter to just this OS type to run",
choices=["linux", "darwin", "windows"],
action="store",
dest="ostype",
default=None,
)
def run_project_cmd(self, args, loader, manifest):
if args.ostype:
loader.build_opts.host_type.ostype = args.ostype
self.create_builder(loader, manifest).printenv(reconfigure=False)
@cmd("generate-github-actions", "generate a GitHub actions configuration")
class GenerateGitHubActionsCmd(ProjectCmdBase):
RUN_ON_ALL = """ [push, pull_request]"""
WORKFLOW_DISPATCH_TMATE = """
workflow_dispatch:
inputs:
tmate_enabled:
description: 'Start a tmate SSH session on failure'
required: false
default: false
type: boolean"""
def run_project_cmd(self, args, loader, manifest):
platforms = [
HostType("linux", "ubuntu", "24"),
HostType("darwin", None, None),
HostType("windows", None, None),
]
for p in platforms:
if args.os_types and p.ostype not in args.os_types:
continue
self.write_job_for_platform(p, args)
def get_run_on(self, args):
if args.run_on_all_branches:
return (
"""
push:
pull_request:"""
+ self.WORKFLOW_DISPATCH_TMATE
)
if args.cron:
if args.cron == "never":
return " {}"
elif args.cron == "workflow_dispatch":
return self.WORKFLOW_DISPATCH_TMATE
else:
return (
f"""
schedule:
- cron: '{args.cron}'"""
+ self.WORKFLOW_DISPATCH_TMATE
)
return (
f"""
push:
branches:
- {args.main_branch}
pull_request:
branches:
- {args.main_branch}"""
+ self.WORKFLOW_DISPATCH_TMATE
)
# TODO: Break up complex function
def write_job_for_platform(self, platform, args): # noqa: C901
build_opts = setup_build_options(args, platform)
ctx_gen = build_opts.get_context_generator()
if args.enable_tests:
ctx_gen.set_value_for_project(args.project, "test", "on")
else:
ctx_gen.set_value_for_project(args.project, "test", "off")
loader = ManifestLoader(build_opts, ctx_gen)
self.process_project_dir_arguments(args, loader)
manifest = loader.load_manifest(args.project)
manifest_ctx = loader.ctx_gen.get_context(manifest.name)
run_tests = (
args.enable_tests
and manifest.get("github.actions", "run_tests", ctx=manifest_ctx) != "off"
)
rust_version = (
manifest.get("github.actions", "rust_version", ctx=manifest_ctx) or "stable"
)
override_build_type = args.build_type or manifest.get(
"github.actions", "build_type", ctx=manifest_ctx
)
if run_tests:
manifest_ctx.set("test", "on")
run_on = self.get_run_on(args)
tests_arg = "--no-tests "
if run_tests:
tests_arg = ""
# Some projects don't do anything "useful" as a leaf project, only
# as a dep for a leaf project. Check for those here; we don't want
# to waste the effort scheduling them on CI.
# We do this by looking at the builder type in the manifest file
# rather than creating a builder and checking its type because we
# don't know enough to create the full builder instance here.
builder_name = manifest.get("build", "builder", ctx=manifest_ctx)
if builder_name == "nop":
return None
# We want to be sure that we're running things with python 3
# but python versioning is honestly a bit of a frustrating mess.
# `python` may be version 2 or version 3 depending on the system.
# python3 may not be a thing at all!
# Assume an optimistic default
py3 = "python3"
if build_opts.is_linux():
artifacts = "linux"
if args.runs_on:
runs_on = args.runs_on
else:
runs_on = f"ubuntu-{args.ubuntu_version}"
if args.cpu_cores:
runs_on = f"{args.cpu_cores}-core-ubuntu-{args.ubuntu_version}"
elif build_opts.is_windows():
artifacts = "windows"
if args.runs_on:
runs_on = args.runs_on
else:
runs_on = "windows-2022"
# The windows runners are python 3 by default; python2.exe
# is available if needed.
py3 = "python"
else:
artifacts = "mac"
if args.runs_on:
runs_on = args.runs_on
else:
runs_on = "macOS-latest"
os.makedirs(args.output_dir, exist_ok=True)
job_file_prefix = "getdeps_"
if args.job_file_prefix:
job_file_prefix = args.job_file_prefix
output_file = os.path.join(args.output_dir, f"{job_file_prefix}{artifacts}.yml")
if args.job_name_prefix:
job_name = args.job_name_prefix + artifacts.capitalize()
else:
job_name = artifacts
with open(output_file, "w") as out:
# Deliberate line break here because the @ and the generated
# symbols are meaningful to our internal tooling when they
# appear in a single token
out.write("# This file was @")
out.write("generated by getdeps.py\n")
out.write(
f"""
name: {job_name}
on:{run_on}
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
"""
)
getdepscmd = f"{py3} build/fbcode_builder/getdeps.py"
out.write(" build:\n")
out.write(" runs-on: %s\n" % runs_on)
out.write(" steps:\n")
if build_opts.is_windows():
# cmake relies on BOOST_ROOT but GH deliberately don't set it in order
# to avoid versioning issues:
# https://github.com/actions/virtual-environments/issues/319
# Instead, set the version we think we need; this is effectively
# coupled with the boost manifest
# This is the unusual syntax for setting an env var for the rest of
# the steps in a workflow:
# https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/
out.write(" - name: Export boost environment\n")
out.write(
' run: "echo BOOST_ROOT=%BOOST_ROOT_1_83_0% >> %GITHUB_ENV%"\n'
)
out.write(" shell: cmd\n")
out.write(" - name: Fix Git config\n")
out.write(" run: >\n")
out.write(" git config --system core.longpaths true &&\n")
out.write(" git config --system core.autocrlf false &&\n")
# cxx crate needs symlinks enabled
out.write(" git config --system core.symlinks true\n")
# && is not supported on default windows powershell, so use cmd
out.write(" shell: cmd\n")
out.write(" - uses: actions/checkout@v6\n")
build_type_arg = ""
if override_build_type:
build_type_arg = f"--build-type {override_build_type} "
if args.shared_libs:
build_type_arg += "--shared-libs "
if build_opts.free_up_disk:
free_up_disk = "--free-up-disk "
if not build_opts.is_windows():
out.write(" - name: Show disk space at start\n")
out.write(" run: df -h\n")
# remove the unused github supplied android dev tools
out.write(" - name: Free up disk space\n")
out.write(" run: sudo rm -rf /usr/local/lib/android\n")
out.write(" - name: Show disk space after freeing up\n")
out.write(" run: df -h\n")
else:
free_up_disk = ""
allow_sys_arg = ""
if (
build_opts.allow_system_packages
and build_opts.host_type.get_package_manager()
):
sudo_arg = "sudo --preserve-env=http_proxy "
allow_sys_arg = " --allow-system-packages"
if build_opts.host_type.get_package_manager() == "deb":
out.write(" - name: Update system package info\n")
out.write(f" run: {sudo_arg}apt-get update\n")
out.write(" - name: Install system deps\n")
if build_opts.is_darwin():
# brew is installed as regular user
sudo_arg = ""
system_deps_cmd = f"{sudo_arg}{getdepscmd}{allow_sys_arg} install-system-deps {tests_arg}--recursive {manifest.name}"
if build_opts.is_linux() or build_opts.is_freebsd():
system_deps_cmd += f" && {sudo_arg}{getdepscmd}{allow_sys_arg} install-system-deps {tests_arg}--recursive patchelf"
out.write(f" run: {system_deps_cmd}\n")
required_locales = manifest.get(
"github.actions", "required_locales", ctx=manifest_ctx
)
if (
build_opts.host_type.get_package_manager() == "deb"
and required_locales
):
# ubuntu doesn't include this by default
out.write(" - name: Install locale-gen\n")
out.write(f" run: {sudo_arg}apt-get install locales\n")
for loc in required_locales.split():
out.write(f" - name: Ensure {loc} locale present\n")
out.write(f" run: {sudo_arg}locale-gen {loc}\n")
out.write(" - id: paths\n")
out.write(" name: Query paths\n")
if build_opts.is_windows():
out.write(
f" run: {getdepscmd}{allow_sys_arg} query-paths {tests_arg}--recursive --src-dir=. {manifest.name} >> $env:GITHUB_OUTPUT\n"
)
out.write(" shell: pwsh\n")
else:
out.write(
f' run: {getdepscmd}{allow_sys_arg} query-paths {tests_arg}--recursive --src-dir=. {manifest.name} >> "$GITHUB_OUTPUT"\n'
)
projects = loader.manifests_in_dependency_order()
main_repo_url = manifest.get_repo_url(manifest_ctx)
has_same_repo_dep = False
# Add the rust dep which doesn't have a manifest
for m in projects:
if m == manifest:
continue
mbuilder_name = m.get("build", "builder", ctx=manifest_ctx)
if (
m.name == "rust"
or builder_name == "cargo"
or mbuilder_name == "cargo"
):
out.write(f" - name: Install Rust {rust_version.capitalize()}\n")
out.write(f" uses: dtolnay/rust-toolchain@{rust_version}\n")
break
# Normal deps that have manifests
for m in projects:
if m == manifest or m.name == "rust":
continue
ctx = loader.ctx_gen.get_context(m.name)
if m.get_repo_url(ctx) != main_repo_url:
out.write(" - name: Fetch %s\n" % m.name)
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\n"
)
out.write(
f" run: {getdepscmd}{allow_sys_arg} fetch --no-tests {m.name}\n"
)
for m in projects:
if m == manifest or m.name == "rust":
continue
src_dir_arg = ""
ctx = loader.ctx_gen.get_context(m.name)
if main_repo_url and m.get_repo_url(ctx) == main_repo_url:
# Its in the same repo, so src-dir is also .
src_dir_arg = "--src-dir=. "
has_same_repo_dep = True
if args.use_build_cache and not src_dir_arg:
out.write(f" - name: Restore {m.name} from cache\n")
out.write(f" id: restore_{m.name}\n")
# only need to restore if would build it
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\n"
)
out.write(" uses: actions/cache/restore@v4\n")
out.write(" with:\n")
out.write(
f" path: ${{{{ steps.paths.outputs.{m.name}_INSTALL }}}}\n"
)
out.write(
f" key: ${{{{ steps.paths.outputs.{m.name}_CACHE_KEY }}}}-install\n"
)
out.write(" - name: Build %s\n" % m.name)
if not src_dir_arg:
if args.use_build_cache:
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE && ! steps.restore_{m.name}.outputs.cache-hit }}}}\n"
)
else:
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE }}}}\n"
)
out.write(
f" run: {getdepscmd}{allow_sys_arg} build {build_type_arg}{src_dir_arg}{free_up_disk}--no-tests {m.name}\n"
)
if args.use_build_cache and not src_dir_arg:
out.write(f" - name: Save {m.name} to cache\n")
out.write(" uses: actions/cache/save@v4\n")
out.write(
f" if: ${{{{ steps.paths.outputs.{m.name}_SOURCE && ! steps.restore_{m.name}.outputs.cache-hit }}}}\n"
)
out.write(" with:\n")
out.write(
f" path: ${{{{ steps.paths.outputs.{m.name}_INSTALL }}}}\n"
)
out.write(
f" key: ${{{{ steps.paths.outputs.{m.name}_CACHE_KEY }}}}-install\n"
)
out.write(" - name: Build %s\n" % manifest.name)
project_prefix = ""
if not build_opts.is_windows():
prefix = loader.get_project_install_prefix(manifest) or "/usr/local"
project_prefix = " --project-install-prefix %s:%s" % (
manifest.name,
prefix,
)
# If we have dep from same repo, we already built it and don't want to rebuild it again
no_deps_arg = ""
if has_same_repo_dep:
no_deps_arg = "--no-deps "
out.write(
f" run: {getdepscmd}{allow_sys_arg} build {build_type_arg}{tests_arg}{no_deps_arg}--src-dir=. {manifest.name}{project_prefix}\n"
)
out.write(" - name: Copy artifacts\n")
if build_opts.is_linux():
# Strip debug info from the binaries, but only on linux.
# While the `strip` utility is also available on macOS,
# attempting to strip there results in an error.
# The `strip` utility is not available on Windows.
strip = " --strip"
else:
strip = ""
out.write(
f" run: {getdepscmd}{allow_sys_arg} fixup-dyn-deps{strip} "
f"--src-dir=. {manifest.name} _artifacts/{artifacts}{project_prefix} "
f"--final-install-prefix /usr/local\n"
)
out.write(" - uses: actions/upload-artifact@v6\n")
out.write(" with:\n")
out.write(" name: %s\n" % manifest.name)
out.write(" path: _artifacts\n")
if run_tests:
num_jobs_arg = ""
if args.num_jobs:
num_jobs_arg = f"--num-jobs {args.num_jobs} "
out.write(" - name: Test %s\n" % manifest.name)
out.write(
f" run: {getdepscmd}{allow_sys_arg} test {build_type_arg}{num_jobs_arg}--src-dir=. {manifest.name}{project_prefix}\n"
)
if build_opts.free_up_disk and not build_opts.is_windows():
out.write(" - name: Show disk space at end\n")
out.write(" if: always()\n")
out.write(" run: df -h\n")
out.write(" - name: Setup tmate session\n")
out.write(
" if: failure() && github.event_name == 'workflow_dispatch' && inputs.tmate_enabled\n"
)
out.write(" uses: mxschmitt/action-tmate@v3\n")
def setup_project_cmd_parser(self, parser):
parser.add_argument(
"--disallow-system-packages",
help="Disallow satisfying third party deps from installed system packages",
action="store_true",
default=False,
)
parser.add_argument(
"--output-dir", help="The directory that will contain the yml files"
)
parser.add_argument(
"--run-on-all-branches",
action="store_true",
help="Allow CI to fire on all branches - Handy for testing",
)
parser.add_argument(
"--ubuntu-version", default="24.04", help="Version of Ubuntu to use"
)
parser.add_argument(
"--cpu-cores",
help="Number of CPU cores to use (applicable for Linux OS)",
)
parser.add_argument(
"--runs-on",
help="Allow specifying explicit runs-on: for github actions",
)
parser.add_argument(
"--cron",
help="Specify that the job runs on a cron schedule instead of on pushes. Pass never to disable the action.",
)
parser.add_argument(
"--main-branch",
default="main",
help="Main branch to trigger GitHub Action on",
)
parser.add_argument(
"--os-type",
help="Filter to just this OS type to run",
choices=["linux", "darwin", "windows"],
action="append",
dest="os_types",
default=[],
)
parser.add_argument(
"--job-file-prefix",
type=str,
help="add a prefix to all job file names",
default=None,
)
parser.add_argument(
"--job-name-prefix",
type=str,
help="add a prefix to all job names",
default=None,
)
parser.add_argument(
"--free-up-disk",
help="Remove unused tools and clean up intermediate files if possible to maximise space for the build",
action="store_true",
default=False,
)
parser.add_argument("--build-type", **BUILD_TYPE_ARG)
parser.add_argument(
"--no-build-cache",
action="store_false",
default=True,
dest="use_build_cache",
help="Do not attempt to use the build cache.",
)
def get_arg_var_name(args):
for arg in args:
if arg.startswith("--"):
return arg[2:].replace("-", "_")
raise Exception("unable to determine argument variable name from %r" % (args,))
def parse_args():
# We want to allow common arguments to be specified either before or after
# the subcommand name. In order to do this we add them to the main parser
# and to subcommand parsers. In order for this to work, we need to tell
# argparse that the default value is SUPPRESS, so that the default values
# from the subparser arguments won't override values set by the user from
# the main parser. We maintain our own list of desired defaults in the
# common_defaults dictionary, and manually set those if the argument wasn't
# present at all.
common_args = argparse.ArgumentParser(add_help=False)
common_defaults = {}
def add_common_arg(*args, **kwargs):
var_name = get_arg_var_name(args)
default_value = kwargs.pop("default", None)
common_defaults[var_name] = default_value
kwargs["default"] = argparse.SUPPRESS
common_args.add_argument(*args, **kwargs)
add_common_arg("--scratch-path", help="Where to maintain checkouts and build dirs")
add_common_arg(
"--vcvars-path", default=None, help="Path to the vcvarsall.bat on Windows."
)
add_common_arg(
"--install-prefix",
help=(
"Where the final build products will be installed "
"(default is [scratch-path]/installed)"
),
)
add_common_arg(
"--num-jobs",
type=int,
help=(
"Number of concurrent jobs to use while building. "
"(default=number of cpu cores)"
),
)
add_common_arg(
"--use-shipit",
help="use the real ShipIt instead of the simple shipit transformer",
action="store_true",
default=False,
)
add_common_arg(
"--facebook-internal",
help="Setup the build context as an FB internal build",
action="store_true",
default=None,
)
add_common_arg(
"--no-facebook-internal",
help="Perform a non-FB internal build, even when in an fbsource repository",
action="store_false",
dest="facebook_internal",
)
add_common_arg(
"--shared-libs",
help="Build shared libraries if possible",
action="store_true",
default=False,
)
add_common_arg(
"--extra-cmake-defines",
help=(
"Input json map that contains extra cmake defines to be used "
"when compiling the current project and all its deps. "
'e.g: \'{"CMAKE_CXX_FLAGS": "--bla"}\''
),
)
add_common_arg(
"--allow-system-packages",
help="Allow satisfying third party deps from installed system packages",
action="store_true",
default=False,
)
add_common_arg(
"-v",
"--verbose",
help="Print more output",
action="store_true",
default=False,
)
add_common_arg(
"-su",
"--skip-upload",
help="skip upload steps",
action="store_true",
default=False,
)
add_common_arg(
"--lfs-path",
help="Provide a parent directory for lfs when fbsource is unavailable",
default=None,
)
add_common_arg(
"--build-skip-lfs-download",
action="store_true",
default=False,
help=(
"Download from the URL, rather than LFS. This is useful "
"in cases where the upstream project has uploaded a new "
"version of the archive with a different hash"
),
)
add_common_arg(
"--schedule-type",
nargs="?",
help="Indicates how the build was activated",
)
ap = argparse.ArgumentParser(
description="Get and build dependencies and projects", parents=[common_args]
)
sub = ap.add_subparsers(
# metavar suppresses the long and ugly default list of subcommands on a
# single line. We still render the nicer list below where we would
# have shown the nasty one.
metavar="",
title="Available commands",
help="",
)
add_subcommands(sub, common_args)
args = ap.parse_args()
for var_name, default_value in common_defaults.items():
if not hasattr(args, var_name):
setattr(args, var_name, default_value)
return ap, args
def main():
ap, args = parse_args()
if getattr(args, "func", None) is None:
ap.print_help()
return 0
try:
return args.func(args)
except UsageError as exc:
ap.error(str(exc))
return 1
except TransientFailure as exc:
print("TransientFailure: %s" % str(exc))
# This return code is treated as a retryable transient infrastructure
# error by Facebook's internal CI, rather than eg: a build or code
# related error that needs to be fixed before progress can be made.
return 128
except subprocess.CalledProcessError as exc:
print("%s" % str(exc), file=sys.stderr)
print("!! Failed", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())
================================================
FILE: build/fbcode_builder/manifests/CLI11
================================================
[manifest]
name = CLI11
[download]
url = https://github.com/CLIUtils/CLI11/archive/v2.0.0.tar.gz
sha256 = 2c672f17bf56e8e6223a3bfb74055a946fa7b1ff376510371902adb9cb0ab6a3
[build]
builder = cmake
subdir = CLI11-2.0.0
[cmake.defines]
CLI11_BUILD_TESTS = OFF
CLI11_BUILD_EXAMPLES = OFF
================================================
FILE: build/fbcode_builder/manifests/autoconf
================================================
[manifest]
name = autoconf
[debs]
autoconf
[homebrew]
autoconf
[rpms]
autoconf
[pps]
autoconf
[download]
url = https://ftpmirror.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
sha256 = 954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969
[build]
builder = autoconf
subdir = autoconf-2.69
================================================
FILE: build/fbcode_builder/manifests/automake
================================================
[manifest]
name = automake
[homebrew]
automake
[debs]
automake
[rpms]
automake
[pps]
automake
[download]
url = https://ftpmirror.gnu.org/gnu/automake/automake-1.16.1.tar.gz
sha256 = 608a97523f97db32f1f5d5615c98ca69326ced2054c9f82e65bade7fc4c9dea8
[build]
builder = autoconf
subdir = automake-1.16.1
[dependencies]
autoconf
================================================
FILE: build/fbcode_builder/manifests/benchmark
================================================
[manifest]
name = benchmark
[download]
url = https://github.com/google/benchmark/archive/refs/tags/v1.8.0.tar.gz
sha256 = ea2e94c24ddf6594d15c711c06ccd4486434d9cf3eca954e2af8a20c88f9f172
[build]
builder = cmake
subdir = benchmark-1.8.0/
[cmake.defines]
BENCHMARK_ENABLE_TESTING=OFF
================================================
FILE: build/fbcode_builder/manifests/blake3
================================================
[manifest]
name = blake3
[download]
url = https://github.com/BLAKE3-team/BLAKE3/archive/refs/tags/1.5.1.tar.gz
sha256 = 822cd37f70152e5985433d2c50c8f6b2ec83aaf11aa31be9fe71486a91744f37
[build]
builder = cmake
subdir = BLAKE3-1.5.1/c
================================================
FILE: build/fbcode_builder/manifests/boost
================================================
[manifest]
name = boost
[download.not(os=windows)]
url = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.tar.gz
sha256 = c0685b68dd44cc46574cce86c4e17c0f611b15e195be9848dfd0769a0a207628
[download.os=windows]
url = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.zip
sha256 = c86bd9d9eef795b4b0d3802279419fde5221922805b073b9bd822edecb1ca28e
[preinstalled.env]
# Here we list the acceptable versions that cmake needs a hint to find
BOOST_ROOT_1_69_0
BOOST_ROOT_1_83_0
[debs]
libboost-all-dev
[homebrew]
boost
# Boost cmake detection on homebrew adds this as requirement: https://github.com/Homebrew/homebrew-core/issues/67427#issuecomment-754187345
icu4c
[pps]
boost
[rpms.all(distro=centos_stream,distro_vers=8)]
boost169
boost169-math
boost169-test
boost169-fiber
boost169-graph
boost169-log
boost169-openmpi
boost169-timer
boost169-chrono
boost169-locale
boost169-thread
boost169-atomic
boost169-random
boost169-static
boost169-contract
boost169-date-time
boost169-iostreams
boost169-container
boost169-coroutine
boost169-filesystem
boost169-system
boost169-stacktrace
boost169-regex
boost169-devel
boost169-context
boost169-python3-devel
boost169-type_erasure
boost169-wave
boost169-python3
boost169-serialization
boost169-program-options
[rpms.distro=fedora]
boost-devel
boost-static
[build]
builder = boost
job_weight_mib = 512
patchfile = boost_1_83_0.patch
[b2.args]
--with-atomic
--with-chrono
--with-container
--with-context
--with-contract
--with-coroutine
--with-date_time
--with-exception
--with-fiber
--with-filesystem
--with-graph
--with-graph_parallel
--with-iostreams
--with-locale
--with-log
--with-math
--with-mpi
--with-program_options
--with-python
--with-random
--with-regex
--with-serialization
--with-stacktrace
--with-system
--with-test
--with-thread
--with-timer
--with-type_erasure
[bootstrap.args.os=darwin]
# Not really gcc, but CI puts a broken clang in the PATH, and saying gcc
# here selects the correct one from Xcode.
--with-toolset=gcc
[b2.args.os=linux]
# RHEL hardened gcc is not compatible with PCH
# https://bugzilla.redhat.com/show_bug.cgi?id=1806545
pch=off
[b2.args.os=darwin]
toolset=clang
# Since Xcode 15.3 std::piecewise_construct is only visible in C++17 and later modes
cxxflags="-DBOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT=0"
[b2.args.all(os=windows,fb=on)]
toolset=msvc-14.3
================================================
FILE: build/fbcode_builder/manifests/boost-python
================================================
[manifest]
name = boost-python
[download.not(os=windows)]
url = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.tar.gz
sha256 = c0685b68dd44cc46574cce86c4e17c0f611b15e195be9848dfd0769a0a207628
[download.os=windows]
url = https://archives.boost.io/release/1.83.0/source/boost_1_83_0.zip
sha256 = c86bd9d9eef795b4b0d3802279419fde5221922805b073b9bd822edecb1ca28e
[preinstalled.env]
# Here we list the acceptable versions that cmake needs a hint to find
BOOST_ROOT_1_69_0
BOOST_ROOT_1_83_0
[homebrew]
boost
# Boost cmake detection on homebrew adds this as requirement: https://github.com/Homebrew/homebrew-core/issues/67427#issuecomment-754187345
icu4c
[pps]
boost
[rpms.all(distro=centos_stream,distro_vers=8)]
boost169
boost169-math
boost169-test
boost169-fiber
boost169-graph
boost169-log
boost169-openmpi
boost169-timer
boost169-chrono
boost169-locale
boost169-thread
boost169-atomic
boost169-random
boost169-static
boost169-contract
boost169-date-time
boost169-iostreams
boost169-container
boost169-coroutine
boost169-filesystem
boost169-system
boost169-stacktrace
boost169-regex
boost169-devel
boost169-context
boost169-python3-devel
boost169-type_erasure
boost169-wave
boost169-python3
boost169-serialization
boost169-program-options
[rpms.distro=fedora]
boost-devel
boost-static
[build]
builder = boost
job_weight_mib = 512
patchfile = boost_1_83_0.patch
[build.not(os=linux)]
builder = nop
[b2.args]
--with-atomic
--with-chrono
--with-container
--with-context
--with-contract
--with-coroutine
--with-date_time
--with-exception
--with-fiber
--with-filesystem
--with-graph
--with-graph_parallel
--with-iostreams
--with-locale
--with-log
--with-math
--with-mpi
--with-program_options
--with-python
--with-random
--with-regex
--with-serialization
--with-stacktrace
--with-system
--with-test
--with-thread
--with-timer
--with-type_erasure
[bootstrap.args.os=darwin]
# Not really gcc, but CI puts a broken clang in the PATH, and saying gcc
# here selects the correct one from Xcode.
--with-toolset=gcc
[b2.args.os=linux]
# RHEL hardened gcc is not compatible with PCH
# https://bugzilla.redhat.com/show_bug.cgi?id=1806545
pch=off
# Python extensions need -fPIC for static library linking into shared objects
cxxflags="-fPIC"
[b2.args.os=darwin]
toolset=clang
# Since Xcode 15.3 std::piecewise_construct is only visible in C++17 and later modes
cxxflags="-DBOOST_UNORDERED_HAVE_PIECEWISE_CONSTRUCT=0"
[b2.args.all(os=windows,fb=on)]
toolset=msvc-14.3
================================================
FILE: build/fbcode_builder/manifests/bz2
================================================
[manifest]
name = bz2
[debs]
libbz2-dev
bzip2
[homebrew]
bzip2
[rpms]
bzip2-devel
bzip2
[download]
url = https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz
sha256 = ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269
[build.not(os=windows)]
builder = make
subdir = bzip2-1.0.8
[make.build_args.os=linux]
# python bz2 support on linux needs dynamic library
-f
Makefile-libbz2_so
[make.install_args]
install
[build.os=windows]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/c-ares
================================================
[manifest]
name = c-ares
[download]
url = https://github.com/c-ares/c-ares/releases/download/v1.34.6/c-ares-1.34.6.tar.gz
sha256 = 912dd7cc3b3e8a79c52fd7fb9c0f4ecf0aaa73e45efda880266a2d6e26b84ef5
[build]
builder = cmake
subdir = c-ares-1.34.6
[cmake.defines]
CARES_STATIC = ON
[cmake.defines.shared_libs=off]
CARES_SHARED = OFF
================================================
FILE: build/fbcode_builder/manifests/cabal
================================================
[manifest]
name = cabal
[download.os=linux]
url = https://downloads.haskell.org/~cabal/cabal-install-3.6.2.0/cabal-install-3.6.2.0-x86_64-linux-deb10.tar.xz
sha256 = 4759b56e9257e02f29fa374a6b25d6cb2f9d80c7e3a55d4f678a8e570925641c
[build]
builder = nop
[install.files]
cabal = bin/cabal
================================================
FILE: build/fbcode_builder/manifests/cachelib
================================================
[manifest]
name = cachelib
fbsource_path = fbcode/cachelib
shipit_project = cachelib
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/cachelib.git
[build]
builder = cmake
subdir = cachelib
job_weight_mib = 2048
[dependencies]
zlib
fizz
fmt
folly
fbthrift
googletest
sparsemap
wangle
zstd
mvfst
numa
libaio
magic_enum
# cachelib also depends on openssl but since the latter requires a platform-
# specific configuration we rely on the folly manifest to provide this
# dependency to avoid duplication.
[dependencies.all(distro=centos_stream,distro_vers=9)]
gcc14
[cmake.defines.all(distro=centos_stream,distro_vers=9)]
CMAKE_C_COMPILER=/opt/rh/gcc-toolset-14/root/usr/bin/gcc
CMAKE_CXX_COMPILER=/opt/rh/gcc-toolset-14/root/usr/bin/g++
[shipit.pathmap]
fbcode/cachelib = cachelib
fbcode/cachelib/public_tld = .
[shipit.strip]
^fbcode/cachelib/examples(/|$)
^fbcode/cachelib/facebook(/|$)
^fbcode/cachelib/public_tld/website/docs/facebook(/|$)
^fbcode/cachelib/public_tld/website/node_modules(/|$)
^fbcode/cachelib/public_tld/website/build(/|$)
================================================
FILE: build/fbcode_builder/manifests/cinderx-3_14
================================================
[manifest]
name = cinderx-3_14
fbsource_path = fbcode/cinderx
shipit_project = facebookincubator/cinderx
[git]
repo_url = https://github.com/facebookincubator/cinderx.git
[build.os=linux]
builder = setup-py
[build.not(os=linux)]
builder = nop
[dependencies]
python-setuptools
python-3_14
[shipit.pathmap]
fbcode/cinderx = cinderx
fbcode/cinderx/oss_toplevel = .
[setup-py.test]
python_script = cinderx/PythonLib/test_cinderx/test_oss_quick.py
[setup-py.env]
CINDERX_ENABLE_PGO=1
CINDERX_ENABLE_LTO=1
================================================
FILE: build/fbcode_builder/manifests/cinderx-main
================================================
# For building CinderX against CPython main.
# Note that externally this can be broken because in that environment we will
# be checking out the head of the CPython repo. However CinderX is only built
# and tested against our internal copy of CPython which updates ~daily, and so
# may be behind CPython head.
[manifest]
name = cinderx-main
fbsource_path = fbcode/cinderx
shipit_project = facebookincubator/cinderx
[git]
repo_url = https://github.com/facebookincubator/cinderx.git
[build.os=linux]
builder = setup-py
[build.not(os=linux)]
builder = nop
[dependencies]
python-setuptools
python-main
[shipit.pathmap]
fbcode/cinderx = cinderx
fbcode/cinderx/oss_toplevel = .
[setup-py.test]
python_script = cinderx/PythonLib/test_cinderx/test_oss_quick.py
[setup-py.env]
CINDERX_ENABLE_PGO=1
CINDERX_ENABLE_LTO=1
================================================
FILE: build/fbcode_builder/manifests/clang
================================================
[manifest]
name = clang
[rpms]
clang15-devel
================================================
FILE: build/fbcode_builder/manifests/clang19
================================================
[manifest]
name = clang19
[debs.os=linux]
clang-19
[rpms.os=linux]
clang
================================================
FILE: build/fbcode_builder/manifests/cmake
================================================
[manifest]
name = cmake
[homebrew]
cmake
# 18.04 cmake is too old
[debs.not(all(distro=ubuntu,distro_vers="18.04"))]
cmake
[rpms]
cmake
[pps]
cmake
[dependencies]
ninja
[download.os=windows]
url = https://github.com/Kitware/CMake/releases/download/v3.20.4/cmake-3.20.4-windows-x86_64.zip
sha256 = 965d2f001c3ca807d288f2b6b15c42b25579a0e73ef12c2a72c95f4c69123638
[download.os=darwin]
url = https://github.com/Kitware/CMake/releases/download/v3.20.4/cmake-3.20.4-macos-universal.tar.gz
sha256 = df90016635e3183834143c6d94607f0804fe9762f7cc6032f6a4afd7c19cd43b
[download.any(os=linux,os=freebsd)]
url = https://github.com/Kitware/CMake/releases/download/v3.20.4/cmake-3.20.4.tar.gz
sha256 = 87a4060298f2c6bb09d479de1400bc78195a5b55a65622a7dceeb3d1090a1b16
[build.os=windows]
builder = nop
subdir = cmake-3.20.4-windows-x86_64
[build.os=darwin]
builder = nop
subdir = cmake-3.20.4-macos-universal
[install.files.os=darwin]
CMake.app/Contents/bin = bin
CMake.app/Contents/share = share
[build.any(os=linux,os=freebsd)]
builder = cmakebootstrap
subdir = cmake-3.20.4
[make.install_args.any(os=linux,os=freebsd)]
install
================================================
FILE: build/fbcode_builder/manifests/cpptoml
================================================
[manifest]
name = cpptoml
[homebrew]
cpptoml
[download]
url = https://github.com/chadaustin/cpptoml/archive/refs/tags/v0.1.2.tar.gz
sha256 = beda37e94f9746874436c8090c045fd80ae6f8a51f7c668c932a2b110a4fc277
[build]
builder = cmake
subdir = cpptoml-0.1.2
[cmake.defines.os=freebsd]
ENABLE_LIBCXX=NO
================================================
FILE: build/fbcode_builder/manifests/double-conversion
================================================
[manifest]
name = double-conversion
[download]
url = https://github.com/google/double-conversion/archive/v3.1.4.tar.gz
sha256 = 95004b65e43fefc6100f337a25da27bb99b9ef8d4071a36a33b5e83eb1f82021
[homebrew]
double-conversion
[debs]
libdouble-conversion-dev
[rpms]
double-conversion
double-conversion-devel
[pps]
double-conversion
[build]
builder = cmake
subdir = double-conversion-3.1.4
================================================
FILE: build/fbcode_builder/manifests/double-conversion-python
================================================
[manifest]
name = double-conversion-python
[download]
url = https://github.com/google/double-conversion/archive/v3.1.4.tar.gz
sha256 = 95004b65e43fefc6100f337a25da27bb99b9ef8d4071a36a33b5e83eb1f82021
[homebrew]
double-conversion
[debs]
libdouble-conversion-dev
[rpms]
double-conversion
double-conversion-devel
[pps]
double-conversion
[build]
builder = cmake
subdir = double-conversion-3.1.4
[build.not(os=linux)]
builder = nop
[cmake.defines]
CMAKE_POSITION_INDEPENDENT_CODE=ON
================================================
FILE: build/fbcode_builder/manifests/eden
================================================
[manifest]
name = eden
fbsource_path = fbcode/eden
shipit_project = eden
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/sapling.git
[github.actions]
run_tests = off
[sandcastle]
run_tests = off
[build]
builder = cmake
[dependencies]
blake3
googletest
folly
fbthrift
fb303
cpptoml
rocksdb
re2
libgit2
pexpect
python-psutil
python-toml
python-filelock
edencommon
rust-shed
[dependencies.fbsource=on]
rust
# macOS ships with sqlite3, and some of the core system
# frameworks require that that version be linked rather
# than the one we might build for ourselves here, so we
# skip building it on macos.
[dependencies.not(os=darwin)]
sqlite3
[dependencies.os=darwin]
osxfuse
[dependencies.not(os=windows)]
# TODO: teach getdeps to compile curl on Windows.
# Enabling curl on Windows requires us to find a way to compile libcurl with
# msvc.
libcurl
# Added so that OSS doesn't see system "python" which is python 2 on darwin and some linux
python
# TODO: teach getdeps to compile lmdb on Windows.
lmdb
[dependencies.test=on]
# sapling CLI is needed to run the tests
sapling
[shipit.pathmap.fb=on]
# for internal builds that use getdeps
fbcode/fb303 = fb303
fbcode/common/rust/shed = common/rust/shed
fbcode/thrift/lib/cpp = thrift/lib/cpp
fbcode/thrift/lib/cpp2 = thrift/lib/cpp2
fbcode/thrift/lib/java = thrift/lib/java
fbcode/thrift/lib/py = thrift/lib/py
fbcode/thrift/lib/python = thrift/lib/python
fbcode/thrift/lib/rust = thrift/lib/rust
[shipit.pathmap]
# Map hostcaps for now as eden C++ includes its .h. Rust-shed should install it
fbcode/common/rust/shed/hostcaps = common/rust/shed/hostcaps
fbcode/configerator/structs/scm/hg = configerator/structs/scm/hg
fbcode/eden/oss = .
fbcode/eden = eden
fbcode/tools/lfs = tools/lfs
[shipit.pathmap.fb=off]
fbcode/eden/fs/public_autocargo = eden/fs
fbcode/eden/scm/public_autocargo = eden/scm
fbcode/common/rust/shed/hostcaps/public_cargo = common/rust/shed/hostcaps
fbcode/configerator/structs/scm/hg/public_autocargo = configerator/structs/scm/hg
[shipit.strip]
^fbcode/eden/addons/.*$
^fbcode/eden/fs/eden-config\.h$
^fbcode/eden/fs/py/eden/config\.py$
^fbcode/eden/hg-server/.*$
^fbcode/eden/mononoke/(?!lfs_protocol)
^fbcode/eden/scm/build/.*$
^fbcode/eden/scm/lib/third-party/rust/.*/Cargo.toml$
^fbcode/eden/website/.*$
^fbcode/eden/.*/\.cargo/.*$
/Cargo\.lock$
\.pyc$
[shipit.strip.fb=off]
^fbcode/common/rust/shed(?!/public_autocargo).*/Cargo\.toml$
^fbcode/configerator/structs/scm/hg(?!/public_autocargo).*/Cargo\.toml$
^fbcode/eden/fs(?!/public_autocargo).*/Cargo\.toml$
^fbcode/eden/scm(?!/public_autocargo|/saplingnative).*/Cargo\.toml$
^.*/facebook/.*$
^.*/fb/.*$
[cmake.defines.all(fb=on,os=windows)]
ENABLE_GIT=OFF
INSTALL_PYTHON_LIB=ON
[cmake.defines.all(not(fb=on),os=windows)]
ENABLE_GIT=OFF
[cmake.defines.fbsource=on]
USE_CARGO_VENDOR=ON
[cmake.defines.fb=on]
IS_FB_BUILD=ON
[depends.environment]
EDEN_VERSION_OVERRIDE
================================================
FILE: build/fbcode_builder/manifests/edencommon
================================================
[manifest]
name = edencommon
fbsource_path = fbcode/eden/common
shipit_project = edencommon
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebookexperimental/edencommon.git
[build]
builder = cmake
[dependencies]
fbthrift
fb303
fmt
folly
gflags
glog
[cmake.defines.test=on]
BUILD_TESTS=ON
[cmake.defines.test=off]
BUILD_TESTS=OFF
[shipit.pathmap]
fbcode/eden/common = eden/common
fbcode/eden/common/oss = .
[shipit.strip]
@README.facebook@
================================================
FILE: build/fbcode_builder/manifests/exprtk
================================================
[manifest]
name = exprtk
[download]
url = https://github.com/ArashPartow/exprtk/archive/refs/tags/0.0.1.tar.gz
sha256 = fb72791c88ae3b3426e14fdad630027715682584daf56b973569718c56e33f28
[build.not(os=windows)]
builder = nop
subdir = exprtk-0.0.1
[install.files]
exprtk.hpp = exprtk.hpp
[dependencies]
================================================
FILE: build/fbcode_builder/manifests/fast_float
================================================
[manifest]
name = fast_float
[download]
url = https://github.com/fastfloat/fast_float/archive/refs/tags/v8.0.0.tar.gz
sha256 = f312f2dc34c61e665f4b132c0307d6f70ad9420185fa831911bc24408acf625d
[build]
builder = cmake
subdir = fast_float-8.0.0
[cmake.defines]
FASTFLOAT_TEST = OFF
FASTFLOAT_SANITIZE = OFF
[debs.not(all(distro=ubuntu,any(distro_vers="18.04",distro_vers="20.04",distro_vers="22.04",distro_vers="24.04")))]
libfast-float-dev
[rpms.distro=fedora]
fast_float-devel
================================================
FILE: build/fbcode_builder/manifests/fatal
================================================
[manifest]
name = fatal
fbsource_path = fbcode/fatal
shipit_project = fatal
[git]
repo_url = https://github.com/facebook/fatal.git
[shipit.pathmap]
fbcode/fatal = fatal
fbcode/fatal/public_tld = .
[build]
builder = nop
subdir = .
[install.files]
fatal/portability.h = fatal/portability.h
fatal/preprocessor.h = fatal/preprocessor.h
fatal/container = fatal/container
fatal/functional = fatal/functional
fatal/math = fatal/math
fatal/string = fatal/string
fatal/type = fatal/type
================================================
FILE: build/fbcode_builder/manifests/fb303
================================================
[manifest]
name = fb303
fbsource_path = fbcode/fb303
shipit_project = fb303
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/fb303.git
[cargo]
cargo_config_file = source/fb303/thrift/.cargo/config.toml
[crate.pathmap]
fb303_core = fb303/thrift/rust
[build]
builder = cmake
[dependencies]
folly
gflags
glog
fbthrift
[cmake.defines.test=on]
BUILD_TESTS=ON
[cmake.defines.test=off]
BUILD_TESTS=OFF
[shipit.pathmap]
fbcode/fb303/github = .
fbcode/fb303/public_autocargo = fb303
fbcode/fb303 = fb303
[shipit.strip]
^fbcode/fb303/(?!public_autocargo).+/Cargo\.toml$
================================================
FILE: build/fbcode_builder/manifests/fboss
================================================
[manifest]
name = fboss
fbsource_path = fbcode/fboss
shipit_project = fboss
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/fboss.git
[build.os=linux]
builder = cmake
# fboss files take a lot of RAM to compile.
job_weight_mib = 3072
[build.not(os=linux)]
builder = nop
[dependencies]
folly
fb303
wangle
fizz
mvfst
fmt
libsodium
googletest
zstd
fatal
fbthrift
iproute2
libusb
libcurl
libnl
libsai
re2
python
yaml-cpp
libyaml
CLI11
exprtk
nlohmann-json
libgpiod
systemd
range-v3
tabulate
gcc12
python-pyyaml
# ShipitPathMap always assume to use directory to fetch the source code.
# If you need to sync the files to fboss github repo, please make changes in
# configerator/source/opensource/shipit_config/facebook/fboss.cconf
[shipit.pathmap]
fbcode/fboss/github = .
fbcode/fboss/common = common
fbcode/fboss = fboss
# NOTE: Although this directory has other thrift files might not be ready for
# opensource, this pathmap should only be used internally `getdeps.py fetch`
fbcode/configerator/structs/neteng/fboss/thrift = configerator/structs/neteng/fboss/thrift
[shipit.strip]
^fbcode/fboss/github/docs/.*
^fbcode/fboss/oss/.*
^fbcode/fboss/github/.github/.*
^fbcode/fboss/github/fboss-image/.*
^fbcode/fboss/github/.pre-commit-config.yaml
^fbcode/fboss/github/requirements-dev.txt
^fbcode/fboss/.llms/.*
[sandcastle]
run_tests = off
================================================
FILE: build/fbcode_builder/manifests/fbthrift
================================================
[manifest]
name = fbthrift
fbsource_path = xplat/thrift
shipit_project = fbthrift
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/fbthrift.git
[cargo]
cargo_config_file = source/thrift/lib/rust/.cargo/config.toml
[crate.pathmap]
fbthrift = thrift/lib/rust
[build]
builder = cmake
job_weight_mib = 2048
[cmake.defines.all(not(os=windows),test=on)]
enable_tests=ON
[cmake.defines.any(os=windows,test=off)]
enable_tests=OFF
[dependencies]
fizz
fmt
folly
googletest
libsodium
wangle
zstd
mvfst
xxhash
# Thrift also depends on openssl but since the latter requires a platform-
# specific configuration we rely on the folly manifest to provide this
# dependency to avoid duplication.
[shipit.pathmap]
xplat/thrift/public_tld = .
xplat/thrift = thrift
[shipit.strip]
^xplat/thrift/thrift-config\.h$
^xplat/thrift/perf/canary.py$
^xplat/thrift/perf/loadtest.py$
^xplat/thrift/.castle/.*
================================================
FILE: build/fbcode_builder/manifests/fbthrift-python
================================================
[manifest]
name = fbthrift-python
fbsource_path = xplat/thrift
shipit_project = fbthrift
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/fbthrift.git
[cargo]
cargo_config_file = source/thrift/lib/rust/.cargo/config.toml
[crate.pathmap]
fbthrift = thrift/lib/rust
[build]
builder = cmake
job_weight_mib = 2048
[build.not(os=linux)]
builder = nop
[cmake.defines.all(not(os=windows),test=on)]
enable_tests=ON
[cmake.defines.any(os=windows,test=off)]
enable_tests=OFF
[cmake.defines.os=linux]
thrift_python=ON
enable_tests=ON
[dependencies]
fizz-python
fmt-python
folly-python
googletest
libsodium
wangle-python
zstd-python
mvfst-python
xxhash
# Thrift also depends on openssl but since the latter requires a platform-
# specific configuration we rely on the folly manifest to provide this
# dependency to avoid duplication.
[dependencies.os=linux]
libaio-python
libevent-python
[shipit.pathmap]
xplat/thrift/public_tld = .
xplat/thrift = thrift
[shipit.strip]
^xplat/thrift/thrift-config\.h$
^xplat/thrift/perf/canary.py$
^xplat/thrift/perf/loadtest.py$
^xplat/thrift/.castle/.*
================================================
FILE: build/fbcode_builder/manifests/fizz
================================================
[manifest]
name = fizz
fbsource_path = fbcode/fizz
shipit_project = fizz
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebookincubator/fizz.git
[build]
builder = cmake
subdir = fizz
[cmake.defines]
BUILD_EXAMPLES = OFF
[cmake.defines.test=on]
BUILD_TESTS = ON
[cmake.defines.all(os=windows, test=on)]
BUILD_TESTS = OFF
[cmake.defines.test=off]
BUILD_TESTS = OFF
[dependencies]
folly
liboqs
libsodium
zlib
zstd
[dependencies.all(test=on, not(os=windows))]
googletest
[shipit.pathmap]
fbcode/fizz/public_tld = .
fbcode/fizz = fizz
================================================
FILE: build/fbcode_builder/manifests/fizz-python
================================================
[manifest]
name = fizz-python
fbsource_path = fbcode/fizz
shipit_project = fizz
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebookincubator/fizz.git
[build]
builder = cmake
subdir = fizz
[build.not(os=linux)]
builder = nop
[cmake.defines]
BUILD_EXAMPLES = OFF
[cmake.defines.os=linux]
CMAKE_POSITION_INDEPENDENT_CODE = ON
BUILD_SHARED_LIBS = ON
[cmake.defines.test=on]
BUILD_TESTS = ON
[cmake.defines.all(os=windows, test=on)]
BUILD_TESTS = OFF
[cmake.defines.test=off]
BUILD_TESTS = OFF
[dependencies]
folly-python
liboqs
libsodium
zlib-python
zstd-python
[dependencies.all(test=on, not(os=windows))]
googletest
[shipit.pathmap]
fbcode/fizz/public_tld = .
fbcode/fizz = fizz
================================================
FILE: build/fbcode_builder/manifests/fmt
================================================
[manifest]
name = fmt
[download]
url = https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz
sha256 = ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea
[build]
builder = cmake
subdir = fmt-12.1.0
[cmake.defines]
FMT_TEST = OFF
FMT_DOC = OFF
[homebrew]
fmt
[rpms.distro=fedora]
fmt-devel
================================================
FILE: build/fbcode_builder/manifests/fmt-python
================================================
[manifest]
name = fmt-python
[download]
url = https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz
sha256 = ea7de4299689e12b6dddd392f9896f08fb0777ac7168897a244a6d6085043fea
[build]
builder = cmake
subdir = fmt-12.1.0
[build.not(os=linux)]
builder = nop
[cmake.defines]
FMT_TEST = OFF
FMT_DOC = OFF
# Build as shared library so Python extensions can find fmt symbols at runtime
# (fmt uses -fvisibility=hidden, so static linking leaves symbols unexported)
BUILD_SHARED_LIBS = ON
[homebrew]
fmt
[rpms.distro=fedora]
fmt-devel
================================================
FILE: build/fbcode_builder/manifests/folly
================================================
[manifest]
name = folly
fbsource_path = fbcode/folly
shipit_project = folly
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/folly.git
[build]
builder = cmake
job_weight_mib = 1024
[dependencies]
gflags
glog
googletest
boost
libdwarf
libevent
libsodium
double-conversion
fast_float
fmt
lz4
snappy
zstd
# no openssl or zlib in the linux case, why?
# these are usually installed on the system
# and are the easiest system deps to pull in.
# In the future we want to be able to express
# that a system dep is sufficient in the manifest
# for eg: openssl and zlib, but for now we don't
# have it.
# macOS doesn't expose the openssl api so we need
# to build our own.
[dependencies.os=darwin]
openssl
# Windows has neither openssl nor zlib, so we get
# to provide both
[dependencies.os=windows]
openssl
zlib
[dependencies.os=linux]
libaio
libiberty
libunwind
# xz depends on autoconf which does not build on
# Windows
[dependencies.not(os=windows)]
xz
[shipit.pathmap]
fbcode/folly/public_tld = .
fbcode/folly = folly
[shipit.strip]
^fbcode/folly/folly-config\.h$
^fbcode/folly/public_tld/build/facebook_.*
[cmake.defines]
BUILD_SHARED_LIBS=OFF
[cmake.defines.not(os=windows)]
BOOST_LINK_STATIC=ON
[cmake.defines.os=freebsd]
LIBDWARF_FOUND=NO
[cmake.defines.test=on]
BUILD_TESTS=ON
BUILD_BENCHMARKS=OFF
[cmake.defines.test=off]
BUILD_TESTS=OFF
BUILD_BENCHMARKS=OFF
================================================
FILE: build/fbcode_builder/manifests/folly-python
================================================
[manifest]
name = folly-python
fbsource_path = fbcode/folly
shipit_project = folly
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/folly.git
[build]
builder = cmake
job_weight_mib = 1024
[build.not(os=linux)]
builder = nop
[dependencies]
gflags
glog
googletest
boost-python
libdwarf-python
libevent-python
libsodium
double-conversion-python
fast_float
fmt-python
lz4-python
snappy
zstd-python
# no openssl or zlib in the linux case, why?
# these are usually installed on the system
# and are the easiest system deps to pull in.
# In the future we want to be able to express
# that a system dep is sufficient in the manifest
# for eg: openssl and zlib, but for now we don't
# have it.
# macOS doesn't expose the openssl api so we need
# to build our own.
[dependencies.os=darwin]
openssl
# Windows has neither openssl nor zlib, so we get
# to provide both
[dependencies.os=windows]
openssl
zlib
[dependencies.os=linux]
libaio-python
libiberty-python
libunwind
# xz depends on autoconf which does not build on
# Windows
[dependencies.not(os=windows)]
xz
[shipit.pathmap]
fbcode/folly/public_tld = .
fbcode/folly = folly
[shipit.strip]
^fbcode/folly/folly-config\.h$
^fbcode/folly/public_tld/build/facebook_.*
[cmake.defines.os=linux]
PYTHON_EXTENSIONS=ON
BUILD_SHARED_LIBS=ON
[cmake.defines.not(os=windows)]
BOOST_LINK_STATIC=ON
[cmake.defines.os=freebsd]
LIBDWARF_FOUND=NO
[cmake.defines.test=on]
BUILD_TESTS=ON
BUILD_BENCHMARKS=OFF
[cmake.defines.test=off]
BUILD_TESTS=OFF
BUILD_BENCHMARKS=OFF
================================================
FILE: build/fbcode_builder/manifests/gcc12
================================================
[manifest]
name = gcc12
[rpms.all(distro=centos_stream,distro_vers=9)]
gcc-toolset-12
================================================
FILE: build/fbcode_builder/manifests/gcc14
================================================
[manifest]
name = gcc14
[rpms.all(distro=centos_stream,distro_vers=9)]
gcc-toolset-14
================================================
FILE: build/fbcode_builder/manifests/gflags
================================================
[manifest]
name = gflags
[download]
url = https://github.com/gflags/gflags/archive/v2.2.2.tar.gz
sha256 = 34af2f15cf7367513b352bdcd2493ab14ce43692d2dcd9dfc499492966c64dcf
[build]
builder = cmake
subdir = gflags-2.2.2
[cmake.defines]
BUILD_SHARED_LIBS = ON
BUILD_STATIC_LIBS = ON
#BUILD_gflags_nothreads_LIB = OFF
BUILD_gflags_LIB = ON
[homebrew]
gflags
[debs]
libgflags-dev
[rpms.distro=fedora]
gflags-devel
================================================
FILE: build/fbcode_builder/manifests/ghc
================================================
[manifest]
name = ghc
[download.os=linux]
url = https://downloads.haskell.org/~ghc/9.2.8/ghc-9.2.8-x86_64-fedora27-linux.tar.xz
sha256 = 845f63cd365317bb764d81025554a2527dbe315d6fa268c9859e21b911bf2d3c
[build]
builder = autoconf
subdir = ghc-9.2.8
build_in_src_dir = true
only_install = true
[make.install_args]
install
================================================
FILE: build/fbcode_builder/manifests/git-lfs
================================================
[manifest]
name = git-lfs
[rpms]
git-lfs
[debs]
git-lfs
[homebrew]
git-lfs
# only used from system packages currently
[build]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/glean
================================================
[manifest]
name = glean
fbsource_path = fbcode/glean
shipit_project = facebookincubator/Glean
use_shipit = true
[shipit.pathmap]
# These are only used by target determinator to trigger builds, the
# real path mappings are in the ShipIt config.
fbcode/glean = glean
fbcode/common/hs = hsthrift
[subprojects]
hsthrift = hsthrift
[dependencies]
cabal
ghc
gflags
glog
folly
rocksdb
xxhash
llvm
clang
re2
[build]
builder = make
[make.build_args]
setup-folly
setup-folly-version
cabal-update
all
glean-hie
glass
glean-clang
EXTRA_GHC_OPTS=-j4 +RTS -A32m -n4m -RTS
CABAL_CONFIG_FLAGS=-f-hack-tests -f-typescript-tests -f-python-tests -f-dotnet-tests -f-go-tests -f-rust-tests -f-java-lsif-tests -f-flow-tests -f-bundled-folly
[make.install_args]
install
[make.test_args]
test
EXTRA_GHC_OPTS=-j4 +RTS -A32m -n4m -RTS
CABAL_CONFIG_FLAGS=-f-hack-tests -f-typescript-tests -f-python-tests -f-dotnet-tests -f-go-tests -f-rust-tests -f-java-lsif-tests -f-flow-tests -f-bundled-folly
================================================
FILE: build/fbcode_builder/manifests/glog
================================================
[manifest]
name = glog
[download]
url = https://github.com/google/glog/archive/v0.5.0.tar.gz
sha256 = eede71f28371bf39aa69b45de23b329d37214016e2055269b3b5e7cfd40b59f5
[build]
builder = cmake
subdir = glog-0.5.0
[dependencies]
gflags
[cmake.defines]
BUILD_SHARED_LIBS=ON
BUILD_TESTING=NO
WITH_PKGCONFIG=ON
[cmake.defines.os=freebsd]
HAVE_TR1_UNORDERED_MAP=OFF
HAVE_TR1_UNORDERED_SET=OFF
[homebrew]
glog
# on ubuntu glog brings in liblzma-dev, which in turn breaks watchman tests
[debs.not(distro=ubuntu)]
libgoogle-glog-dev
[rpms.distro=fedora]
glog-devel
================================================
FILE: build/fbcode_builder/manifests/googletest
================================================
[manifest]
name = googletest
[download]
url = https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz
sha256 = 65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c
[build]
builder = cmake
subdir = googletest-1.17.0
[cmake.defines]
# Everything else defaults to the shared runtime, so tell gtest that
# it should not use its choice of the static runtime
gtest_force_shared_crt=ON
[cmake.defines.os=windows]
BUILD_SHARED_LIBS=ON
[homebrew]
googletest
# packaged googletest is too old
[debs.not(all(distro=ubuntu,any(distro_vers="18.04",distro_vers="20.04",distro_vers="22.04")))]
libgtest-dev
libgmock-dev
[rpms.distro=fedora]
gmock-devel
gtest-devel
================================================
FILE: build/fbcode_builder/manifests/gperf
================================================
[manifest]
name = gperf
[download]
url = https://ftpmirror.gnu.org/gnu/gperf/gperf-3.1.tar.gz
sha256 = 588546b945bba4b70b6a3a616e80b4ab466e3f33024a352fc2198112cdbb3ae2
[build.not(os=windows)]
builder = autoconf
subdir = gperf-3.1
[build.os=windows]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/hexdump
================================================
[manifest]
name = hexdump
[rpms]
util-linux
[debs]
bsdmainutils
# only used from system packages currently
[build]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/hsthrift
================================================
[manifest]
name = hsthrift
fbsource_path = fbcode/common/hs
shipit_project = facebookincubator/hsthrift
use_shipit = true
[shipit.pathmap]
# These are only used by target determinator to trigger builds, the
# real path mappings are in the ShipIt config.
fbcode/common/hs = .
[dependencies]
cabal
ghc
gflags
glog
folly
fbthrift
wangle
fizz
boost
[build]
builder = make
[make.build_args]
setup-folly
setup-meta
cabal-update
all
[make.install_args]
install
[make.test_args]
test
================================================
FILE: build/fbcode_builder/manifests/iproute2
================================================
[manifest]
name = iproute2
[download]
url = https://mirrors.edge.kernel.org/pub/linux/utils/net/iproute2/iproute2-4.12.0.tar.gz
sha256 = 46612a1e2d01bb31932557bccdb1b8618cae9a439dfffc08ef35ed8e197f14ce
[build.os=linux]
builder = iproute2
subdir = iproute2-4.12.0
patchfile = iproute2_oss.patch
[build.not(os=linux)]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/jom
================================================
# jom is compatible with MSVC nmake, but adds the /j argment which
# speeds up openssl build a lot
[manifest]
name = jom
# see https://download.qt.io/official_releases/jom/changelog.txt for latest version
[download.os=windows]
url = https://download.qt.io/official_releases/jom/jom_1_1_4.zip
sha256 = d533c1ef49214229681e90196ed2094691e8c4a0a0bef0b2c901debcb562682b
[build.os=windows]
builder = nop
[install.files.os=windows]
. = bin
================================================
FILE: build/fbcode_builder/manifests/jq
================================================
[manifest]
name = jq
[rpms.distro=fedora]
jq
[homebrew]
jq
[download.not(os=windows)]
# we use jq-1.7+ to get fix for number truncation https://github.com/jqlang/jq/pull/1752
url = https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-1.7.1.tar.gz
sha256 = 478c9ca129fd2e3443fe27314b455e211e0d8c60bc8ff7df703873deeee580c2
[build.not(os=windows)]
builder = autoconf
subdir = jq-1.7.1
[build.os=windows]
builder = nop
[autoconf.args]
# This argument turns off some developers tool and it is recommended in jq's
# README
--disable-maintainer-mode
================================================
FILE: build/fbcode_builder/manifests/katran
================================================
[manifest]
name = katran
fbsource_path = fbcode/katran
shipit_project = katran
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebookincubator/katran.git
[build.not(os=linux)]
builder = nop
[build.os=linux]
builder = cmake
subdir = .
[cmake.defines.test=on]
BUILD_TESTS=ON
[cmake.defines.test=off]
BUILD_TESTS=OFF
[dependencies]
folly
fizz
libbpf
libmnl
zlib
googletest
fmt
[debs]
libssl-dev
[shipit.pathmap]
fbcode/katran/public_root = .
fbcode/katran = katran
[shipit.strip]
^fbcode/katran/facebook
^fbcode/katran/OSS_SYNC
================================================
FILE: build/fbcode_builder/manifests/libaio
================================================
[manifest]
name = libaio
[debs]
libaio-dev
[rpms.distro=centos_stream]
libaio-devel
[download]
url = https://pagure.io/libaio/archive/libaio-0.3.113/libaio-libaio-0.3.113.tar.gz
sha256 = 716c7059703247344eb066b54ecbc3ca2134f0103307192e6c2b7dab5f9528ab
[build]
builder = make
subdir = libaio-libaio-0.3.113
[make.build_args.shared_libs=off]
ENABLE_SHARED=0
[make.install_args]
install
[make.install_args.shared_libs=off]
ENABLE_SHARED=0
================================================
FILE: build/fbcode_builder/manifests/libaio-python
================================================
[manifest]
name = libaio-python
[debs]
libaio-dev
[rpms.distro=centos_stream]
libaio-devel
[download]
url = https://pagure.io/libaio/archive/libaio-0.3.113/libaio-libaio-0.3.113.tar.gz
sha256 = 716c7059703247344eb066b54ecbc3ca2134f0103307192e6c2b7dab5f9528ab
[build]
builder = make
subdir = libaio-libaio-0.3.113
[build.not(os=linux)]
builder = nop
[make.build_args]
CFLAGS=-fPIC -O2
[make.install_args]
install
================================================
FILE: build/fbcode_builder/manifests/libbpf
================================================
[manifest]
name = libbpf
[download]
url = https://github.com/libbpf/libbpf/archive/refs/tags/v1.6.2.tar.gz
sha256 = 16f31349c70764cba8e0fad3725cc9f52f6cf952554326aa0229daaa21ef4fbd
# BPF only builds on linux, so make it a NOP on other platforms
[build.not(os=linux)]
builder = nop
[build.os=linux]
builder = make
subdir = libbpf-1.6.2/src
[make.build_args]
BUILD_STATIC_ONLY=y
# libbpf-0.3 requires uapi headers >= 5.8
[make.install_args]
install
install_uapi_headers
BUILD_STATIC_ONLY=y
[dependencies]
libelf
================================================
FILE: build/fbcode_builder/manifests/libcurl
================================================
[manifest]
name = libcurl
[rpms]
libcurl-devel
libcurl-minimal
[debs]
libcurl4-openssl-dev
[pps]
libcurl-gnutls
[download]
url = https://curl.haxx.se/download/curl-7.65.1.tar.gz
sha256 = 821aeb78421375f70e55381c9ad2474bf279fc454b791b7e95fc83562951c690
[dependencies]
nghttp2
# We use system OpenSSL on Linux (see folly's manifest for details)
[dependencies.not(os=linux)]
openssl
[build.not(os=windows)]
builder = autoconf
subdir = curl-7.65.1
[autoconf.args]
# fboss (which added the libcurl dep) doesn't need ldap so it is disabled here.
# if someone in the future wants to add ldap for something else, it won't hurt
# fboss. However, that would require adding an ldap manifest.
#
# For the same reason, we disable libssh2 and libidn2 which aren't really used
# but would require adding manifests if we don't disable them.
--disable-ldap
--without-libssh2
--without-libidn2
[build.os=windows]
builder = cmake
subdir = curl-7.65.1
================================================
FILE: build/fbcode_builder/manifests/libdwarf
================================================
[manifest]
name = libdwarf
[rpms]
libdwarf-devel
libdwarf
[debs]
libdwarf-dev
[homebrew]
dwarfutils
[download]
url = https://www.prevanders.net/libdwarf-0.9.2.tar.xz
sha256 = 22b66d06831a76f6a062126cdcad3fcc58540b89a1acb23c99f8861f50999ec3
[build]
builder = cmake
subdir = libdwarf-0.9.2
================================================
FILE: build/fbcode_builder/manifests/libdwarf-python
================================================
[manifest]
name = libdwarf-python
[rpms]
libdwarf-devel
libdwarf
[debs]
libdwarf-dev
[homebrew]
dwarfutils
[download]
url = https://www.prevanders.net/libdwarf-0.9.2.tar.xz
sha256 = 22b66d06831a76f6a062126cdcad3fcc58540b89a1acb23c99f8861f50999ec3
[build]
builder = cmake
subdir = libdwarf-0.9.2
[build.not(os=linux)]
builder = nop
[cmake.defines]
CMAKE_POSITION_INDEPENDENT_CODE=ON
================================================
FILE: build/fbcode_builder/manifests/libelf
================================================
[manifest]
name = libelf
[rpms]
elfutils-libelf-devel-static
[debs]
libelf-dev
[pps]
libelf
[download]
url = https://sourceware.org/elfutils/ftp/0.193/elfutils-0.193.tar.bz2
sha256 = 7857f44b624f4d8d421df851aaae7b1402cfe6bcdd2d8049f15fc07d3dde7635
# libelf only makes sense on linux, so make it a NOP on other platforms
[build.not(os=linux)]
builder = nop
[build.os=linux]
builder = autoconf
subdir = elfutils-0.193
================================================
FILE: build/fbcode_builder/manifests/libevent
================================================
[manifest]
name = libevent
[debs]
libevent-dev
[homebrew]
libevent
[rpms]
libevent-devel
[pps]
libevent
# Note that the CMakeLists.txt file is present only in
# git repo and not in the release tarball, so take care
# to use the github generated source tarball rather than
# the explicitly uploaded source tarball
[download]
url = https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
sha256 = 92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb
[build]
builder = cmake
subdir = libevent-2.1.12-stable
[cmake.defines]
EVENT__DISABLE_TESTS = ON
EVENT__DISABLE_BENCHMARK = ON
EVENT__DISABLE_SAMPLES = ON
EVENT__DISABLE_REGRESS = ON
[cmake.defines.shared_libs=on]
EVENT__BUILD_SHARED_LIBRARIES = ON
[cmake.defines.shared_libs=off]
EVENT__LIBRARY_TYPE = STATIC
[cmake.defines.os=windows]
EVENT__LIBRARY_TYPE = STATIC
[dependencies.not(any(os=linux, os=freebsd))]
openssl
================================================
FILE: build/fbcode_builder/manifests/libevent-python
================================================
[manifest]
name = libevent-python
# NOTE: System packages (debs, rpms) removed because they don't include
# LibeventConfig.cmake which is required by find_package(Libevent REQUIRED CONFIG).
# Building from source ensures CMake config files are present.
[homebrew]
libevent
[pps]
libevent
# Note that the CMakeLists.txt file is present only in
# git repo and not in the release tarball, so take care
# to use the github generated source tarball rather than
# the explicitly uploaded source tarball
[download]
url = https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
sha256 = 92e6de1be9ec176428fd2367677e61ceffc2ee1cb119035037a27d346b0403bb
[build]
builder = cmake
subdir = libevent-2.1.12-stable
[build.not(os=linux)]
builder = nop
[cmake.defines]
EVENT__DISABLE_TESTS = ON
EVENT__DISABLE_BENCHMARK = ON
EVENT__DISABLE_SAMPLES = ON
EVENT__DISABLE_REGRESS = ON
CMAKE_POSITION_INDEPENDENT_CODE=ON
[cmake.defines.shared_libs=on]
EVENT__BUILD_SHARED_LIBRARIES = ON
[cmake.defines.os=windows]
EVENT__LIBRARY_TYPE = STATIC
[dependencies.not(any(os=linux, os=freebsd))]
openssl
================================================
FILE: build/fbcode_builder/manifests/libffi
================================================
[manifest]
name = libffi
[debs]
libffi-dev
[homebrew]
libffi
[rpms]
libffi-devel
libffi
[pps]
libffi
[download]
url = https://github.com/libffi/libffi/releases/download/v3.4.2/libffi-3.4.2.tar.gz
sha256 = 540fb721619a6aba3bdeef7d940d8e9e0e6d2c193595bc243241b77ff9e93620
[build]
builder = autoconf
subdir = libffi-3.4.2
================================================
FILE: build/fbcode_builder/manifests/libgit2
================================================
[manifest]
name = libgit2
[homebrew]
libgit2
[rpms]
libgit2-devel
[pps]
libgit2
# Ubuntu 18.04 libgit2 has clash with libcurl4-openssl-dev as it depends on
# libcurl4-gnutls-dev. Should be ok from 20.04 again
# There is a description at https://github.com/r-hub/sysreqsdb/issues/77
[debs.not(all(distro=ubuntu,distro_vers="18.04"))]
libgit2-dev
[download]
url = https://github.com/libgit2/libgit2/archive/v0.28.1.tar.gz
sha256 = 0ca11048795b0d6338f2e57717370208c2c97ad66c6d5eac0c97a8827d13936b
[build]
builder = cmake
subdir = libgit2-0.28.1
[cmake.defines]
# Could turn this on if we also wanted to add a manifest for libssh2
USE_SSH = OFF
BUILD_CLAR = OFF
# Have to build shared to work around annoying problems with cmake
# mis-parsing the frameworks required to link this on macos :-/
BUILD_SHARED_LIBS = ON
================================================
FILE: build/fbcode_builder/manifests/libgpiod
================================================
[manifest]
name = libgpiod
[download]
url = https://cdn.kernel.org/pub/software/libs/libgpiod/libgpiod-1.6.tar.xz
sha256 = 62908023d59e8cbb9137ddd14deec50ced862d8f9b8749f288d3dbe7967151ef
[build]
builder = autoconf
subdir = libgpiod-1.6
================================================
FILE: build/fbcode_builder/manifests/libiberty
================================================
[manifest]
name = libiberty
[rpms]
binutils-devel
binutils
[debs.not(all(distro=ubuntu,distro_vers="24.04"))]
binutils-dev
[debs.all(distro=ubuntu,distro_vers="24.04")]
binutils-x86-64-linux-gnu
[download]
url = https://ftpmirror.gnu.org/gnu/binutils/binutils-2.43.tar.xz
sha256 = b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365
[dependencies]
zlib
[build]
builder = autoconf
subdir = binutils-2.43/libiberty
patchfile = libiberty_install_pic_lib.patch
# only build the parts needed for demangling
# as we still want to use system linker and assembler etc
[autoconf.args]
--enable-install-libiberty
================================================
FILE: build/fbcode_builder/manifests/libiberty-python
================================================
[manifest]
name = libiberty-python
[rpms]
binutils-devel
binutils
[debs.not(all(distro=ubuntu,distro_vers="24.04"))]
binutils-dev
[debs.all(distro=ubuntu,distro_vers="24.04")]
binutils-x86-64-linux-gnu
[download]
url = https://ftpmirror.gnu.org/gnu/binutils/binutils-2.43.tar.xz
sha256 = b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365
[dependencies]
zlib-python
[build]
builder = autoconf
subdir = binutils-2.43/libiberty
patchfile = libiberty_install_pic_lib.patch
[build.not(os=linux)]
builder = nop
# only build the parts needed for demangling
# as we still want to use system linker and assembler etc
[autoconf.args]
--enable-install-libiberty
CFLAGS=-fPIC -O2
PICFLAG=-fPIC
================================================
FILE: build/fbcode_builder/manifests/libibverbs
================================================
[manifest]
name = libibverbs
[debs]
libibverbs-dev
rdma-core
[rpms]
libibverbs
rdma-core-devel
[download]
url = https://github.com/linux-rdma/rdma-core/releases/download/v60.0/rdma-core-60.0.tar.gz
sha256 = 9b1b892e4eaaaa5dfbade07a290fbf5079e39117724fa1ef80d0ad78839328de
[build]
builder = cmake
subdir = rdma-core-60.0
[dependencies]
libnl
[cmake.defines]
NO_MAN_PAGES=1
NO_PYVERBS=1
ENABLE_RESOLVE_NEIGH=0
# Use absolute short path for runtime dir to avoid Unix socket path length limit (108 chars)
CMAKE_INSTALL_RUNDIR=/tmp/ibacm
================================================
FILE: build/fbcode_builder/manifests/libmnl
================================================
[manifest]
name = libmnl
[rpms]
libmnl-devel
# all centos 8 distros are missing this,
# but its in fedora so may be back in a later version
[rpms.not(all(any(distro=centos_stream,distro=centos),distro_vers=8))]
libmnl-static
[debs]
libmnl-dev
[pps]
libmnl
[download]
url = https://www.netfilter.org/pub/libmnl/libmnl-1.0.4.tar.bz2
sha256 = 171f89699f286a5854b72b91d06e8f8e3683064c5901fb09d954a9ab6f551f81
[build.os=linux]
builder = autoconf
subdir = libmnl-1.0.4
================================================
FILE: build/fbcode_builder/manifests/libnl
================================================
[manifest]
name = libnl
[rpms]
libnl3-devel
libnl3
[debs]
libnl-3-dev
libnl-route-3-dev
[pps]
libnl
[download]
url = https://github.com/thom311/libnl/releases/download/libnl3_2_25/libnl-3.2.25.tar.gz
sha256 = 8beb7590674957b931de6b7f81c530b85dc7c1ad8fbda015398bc1e8d1ce8ec5
[build.os=linux]
builder = autoconf
subdir = libnl-3.2.25
================================================
FILE: build/fbcode_builder/manifests/liboqs
================================================
[manifest]
name = liboqs
[download]
url = https://github.com/open-quantum-safe/liboqs/archive/refs/tags/0.12.0.tar.gz
sha256 = df999915204eb1eba311d89e83d1edd3a514d5a07374745d6a9e5b2dd0d59c08
[build]
builder = cmake
subdir = liboqs-0.12.0
[cmake.defines]
OQS_MINIMAL_BUILD = KEM_kyber_512;KEM_kyber_768;KEM_kyber_1024;KEM_ml_kem_512;KEM_ml_kem_768;KEM_ml_kem_1024
[dependencies]
openssl
================================================
FILE: build/fbcode_builder/manifests/libsai
================================================
[manifest]
name = libsai
[download]
url = https://github.com/opencomputeproject/SAI/archive/v1.16.3.tar.gz
sha256 = 5c89cdb6b2e4f1b42ced6b78d43d06d22434ddbf423cdc551f7c2001f12e63d9
[build]
builder = nop
subdir = SAI-1.16.3
[install.files]
inc = include
experimental = experimental
================================================
FILE: build/fbcode_builder/manifests/libsodium
================================================
[manifest]
name = libsodium
[debs]
libsodium-dev
[homebrew]
libsodium
[rpms]
libsodium-devel
libsodium-static
[pps]
libsodium
[download.not(os=windows)]
url = https://github.com/jedisct1/libsodium/releases/download/1.0.20-RELEASE/libsodium-1.0.20.tar.gz
sha256 = ebb65ef6ca439333c2bb41a0c1990587288da07f6c7fd07cb3a18cc18d30ce19
[build.not(os=windows)]
builder = autoconf
subdir = libsodium-1.0.20
[download.os=windows]
url = https://github.com/jedisct1/libsodium/releases/download/1.0.20-RELEASE/libsodium-1.0.20-msvc.zip
sha256 = 2ff97f9e3f5b341bdc808e698057bea1ae454f99e29ff6f9b62e14d0eb1b1baa
[build.os=windows]
builder = nop
[install.files.os=windows]
libsodium/x64/Release/v143/dynamic/libsodium.dll = bin/libsodium.dll
libsodium/x64/Release/v143/dynamic/libsodium.lib = lib/libsodium.lib
libsodium/x64/Release/v143/dynamic/libsodium.exp = lib/libsodium.exp
libsodium/x64/Release/v143/dynamic/libsodium.pdb = lib/libsodium.pdb
libsodium/include = include
[autoconf.args]
--with-pic
================================================
FILE: build/fbcode_builder/manifests/libunwind
================================================
[manifest]
name = libunwind
[rpms]
libunwind-devel
libunwind
# on ubuntu this brings in liblzma-dev, which in turn breaks watchman tests
[debs.not(distro=ubuntu)]
libunwind-dev
# The current libunwind v1.8.1 release has compiler issues with aarch64 (https://github.com/libunwind/libunwind/issues/702).
# This more recent libunwind version (based on the latest commit, not a release version) got it fixed.
[download]
url = https://github.com/libunwind/libunwind/archive/f081cf42917bdd5c428b77850b473f31f81767cf.tar.gz
sha256 = 4ff5c335c02d225491d6c885db827fb5fa505fee4e68b4d7e866efc0087e7264
[build]
builder = autoconf
subdir = libunwind-f081cf42917bdd5c428b77850b473f31f81767cf
[autoconf.args]
--with-pic
================================================
FILE: build/fbcode_builder/manifests/libusb
================================================
[manifest]
name = libusb
[debs]
libusb-1.0-0-dev
[homebrew]
libusb
[rpms]
libusb-devel
libusb
[pps]
libusb
[download]
url = https://github.com/libusb/libusb/releases/download/v1.0.22/libusb-1.0.22.tar.bz2
sha256 = 75aeb9d59a4fdb800d329a545c2e6799f732362193b465ea198f2aa275518157
[build.os=linux]
builder = autoconf
subdir = libusb-1.0.22
[autoconf.args]
# fboss (which added the libusb dep) doesn't need udev so it is disabled here.
# if someone in the future wants to add udev for something else, it won't hurt
# fboss.
--disable-udev
================================================
FILE: build/fbcode_builder/manifests/libyaml
================================================
[manifest]
name = libyaml
[download]
url = https://pyyaml.org/download/libyaml/yaml-0.1.7.tar.gz
sha256 = 8088e457264a98ba451a90b8661fcb4f9d6f478f7265d48322a196cec2480729
[build.os=linux]
builder = autoconf
subdir = yaml-0.1.7
[build.not(os=linux)]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/llvm
================================================
[manifest]
name = llvm
[rpms]
llvm15-devel
================================================
FILE: build/fbcode_builder/manifests/lmdb
================================================
[manifest]
name = lmdb
[build]
builder = make
subdir = lmdb-LMDB_0.9.31/libraries/liblmdb
[download]
url = https://github.com/LMDB/lmdb/archive/refs/tags/LMDB_0.9.31.tar.gz
sha256 = dd70a8c67807b3b8532b3e987b0a4e998962ecc28643e1af5ec77696b081c9b0
[make.build_args]
BUILD_STATIC_ONLY=y
[make.install_args]
install
BUILD_STATIC_ONLY=y
================================================
FILE: build/fbcode_builder/manifests/lz4
================================================
[manifest]
name = lz4
[homebrew]
lz4
[rpms]
lz4-devel
# centos 8 and centos_stream 9 are missing this rpm
[rpms.not(any(all(distro=centos,distro_vers=8),all(distro=centos_stream,distro_vers=9)))]
lz4-static
[debs]
liblz4-dev
[pps]
lz4
[download]
url = https://github.com/lz4/lz4/releases/download/v1.10.0/lz4-1.10.0.tar.gz
sha256 = 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b
[build]
builder = cmake
subdir = lz4-1.10.0/build/cmake
================================================
FILE: build/fbcode_builder/manifests/lz4-python
================================================
[manifest]
name = lz4-python
[homebrew]
lz4
[rpms]
lz4-devel
# centos 8 and centos_stream 9 are missing this rpm
[rpms.not(any(all(distro=centos,distro_vers=8),all(distro=centos_stream,distro_vers=9)))]
lz4-static
[debs]
liblz4-dev
[pps]
lz4
[download]
url = https://github.com/lz4/lz4/releases/download/v1.10.0/lz4-1.10.0.tar.gz
sha256 = 537512904744b35e232912055ccf8ec66d768639ff3abe5788d90d792ec5f48b
[build]
builder = cmake
subdir = lz4-1.10.0/build/cmake
[build.not(os=linux)]
builder = nop
[cmake.defines]
CMAKE_POSITION_INDEPENDENT_CODE=ON
================================================
FILE: build/fbcode_builder/manifests/magic_enum
================================================
[manifest]
name = magic_enum
[download]
url = https://github.com/Neargye/magic_enum/releases/download/v0.9.7/magic_enum-v0.9.7.tar.gz
sha256 = c047bc7ca0b76752168140e7ae9a4a30d72bf6530c196fdfbf5105a39d40cc46
[build]
builder = cmake
[cmake.defines]
MAGIC_ENUM_OPT_BUILD_EXAMPLES = OFF
MAGIC_ENUM_OPT_BUILD_TESTS = OFF
MAGIC_ENUM_OPT_INSTALL = ON
================================================
FILE: build/fbcode_builder/manifests/mcrouter
================================================
[manifest]
name = mcrouter
fbsource_path = fbcode/mcrouter
shipit_project = mcrouter
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/mcrouter.git
[dependencies]
folly
wangle
fizz
fbthrift
mvfst
ragel
gflags
glog
boost
libevent
openssl
zlib
double-conversion
[shipit.pathmap]
fbcode/mcrouter/public_tld = .
fbcode/mcrouter = mcrouter
[shipit.strip]
^fbcode/mcrouter/(.*/)?facebook/
[build]
builder = cmake
[cmake.defines]
BUILD_SHARED_LIBS=OFF
[cmake.defines.test=on]
BUILD_TESTS=ON
[cmake.defines.test=off]
BUILD_TESTS=OFF
================================================
FILE: build/fbcode_builder/manifests/mononoke
================================================
[manifest]
name = mononoke
fbsource_path = fbcode/eden
shipit_project = eden
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/sapling.git
[build.not(os=windows)]
builder = cargo
[build.os=windows]
# building Mononoke on windows is not supported
builder = nop
[cargo]
build_doc = true
workspace_dir = eden/mononoke
[github.actions]
rust_version = 1.91
build_type = MinSizeRel
[shipit.pathmap]
fbcode/configerator/structs/scm/hg = configerator/structs/scm/hg
fbcode/configerator/structs/scm/hg/public_autocargo = configerator/structs/scm/hg
fbcode/configerator/structs/scm/mononoke/public_autocargo = configerator/structs/scm/mononoke
fbcode/configerator/structs/scm/mononoke = configerator/structs/scm/mononoke
fbcode/eden/oss = .
fbcode/eden = eden
fbcode/eden/fs/public_autocargo = eden/fs
fbcode/eden/mononoke/public_autocargo = eden/mononoke
fbcode/eden/scm/public_autocargo = eden/scm
fbcode/tools/lfs = tools/lfs
tools/rust/ossconfigs = .
[shipit.strip]
^fbcode/configerator/structs/scm/hg(?!/public_autocargo).*/Cargo\.toml$
^fbcode/configerator/structs/scm/mononoke(?!/public_autocargo).*/Cargo\.toml$
^fbcode/eden/fs(?!/public_autocargo).*/Cargo\.toml$
^fbcode/eden/scm/lib/third-party/rust/.*/Cargo\.toml$
^fbcode/eden/mononoke(?!/public_autocargo).*/Cargo\.toml$
# strip other scm code unrelated to mononoke to prevent triggering unnecessary checks
^fbcode/eden(?!/mononoke|/scm/(lib|public_autocargo))/.*$
^.*/facebook/.*$
^.*/fb/.*$
[dependencies]
fb303
fbthrift
rust-shed
[dependencies.fb=on]
rust
================================================
FILE: build/fbcode_builder/manifests/mononoke_integration
================================================
[manifest]
name = mononoke_integration
fbsource_path = fbcode/eden
shipit_project = eden
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/sapling.git
[build.not(os=windows)]
builder = make
subdir = eden/mononoke/tests/integration
[build.os=windows]
# building Mononoke on windows is not supported
builder = nop
[make.build_args]
build-getdeps
[make.install_args]
install-getdeps
[make.test_args]
test-getdeps
[shipit.pathmap]
fbcode/eden/mononoke/tests/integration = eden/mononoke/tests/integration
[shipit.strip]
^.*/facebook/.*$
^.*/fb/.*$
[dependencies]
git-lfs
jq
mononoke
nmap
python
python-click
ripgrep
sapling
tree
zstd
[dependencies.os=linux]
sqlite3
================================================
FILE: build/fbcode_builder/manifests/moxygen
================================================
[manifest]
name = moxygen
fbsource_path = fbcode/moxygen
shipit_project = moxygen
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebookexperimental/moxygen.git
[build.os=windows]
builder = nop
[build]
builder = cmake
subdir = .
job_weight_mib = 3072
rewrite_includes = true
[cmake.defines.test=on]
BUILD_TESTS = ON
[cmake.defines.test=off]
BUILD_TESTS = OFF
[dependencies]
folly
fizz
wangle
mvfst
proxygen
[dependencies.test=on]
googletest
[shipit.pathmap]
fbcode/ti/experimental/moxygen/project_root = .
fbcode/ti/experimental/moxygen = moxygen
================================================
FILE: build/fbcode_builder/manifests/mvfst
================================================
[manifest]
name = mvfst
fbsource_path = fbcode/quic
shipit_project = mvfst
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/mvfst.git
[build]
builder = cmake
subdir = .
[cmake.defines.test=on]
BUILD_TESTS = ON
[cmake.defines.all(os=windows, test=on)]
BUILD_TESTS = OFF
[cmake.defines.test=off]
BUILD_TESTS = OFF
[dependencies]
folly
fizz
[dependencies.all(test=on, not(os=windows))]
googletest
[shipit.pathmap]
fbcode/quic/public_root = .
fbcode/quic = quic
================================================
FILE: build/fbcode_builder/manifests/mvfst-python
================================================
[manifest]
name = mvfst-python
fbsource_path = fbcode/quic
shipit_project = mvfst
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/mvfst.git
[build]
builder = cmake
subdir = .
[build.not(os=linux)]
builder = nop
[cmake.defines.test=on]
BUILD_TESTS = ON
[cmake.defines.all(os=windows, test=on)]
BUILD_TESTS = OFF
[cmake.defines.test=off]
BUILD_TESTS = OFF
[cmake.defines.os=linux]
CMAKE_POSITION_INDEPENDENT_CODE = ON
BUILD_SHARED_LIBS = ON
[dependencies]
folly-python
fizz-python
[dependencies.all(test=on, not(os=windows))]
googletest
[shipit.pathmap]
fbcode/quic/public_root = .
fbcode/quic = quic
================================================
FILE: build/fbcode_builder/manifests/ncurses
================================================
[manifest]
name = ncurses
[debs]
libncurses-dev
[homebrew]
ncurses
[rpms]
ncurses-devel
[download]
url = https://ftpmirror.gnu.org/gnu/ncurses/ncurses-6.3.tar.gz
sha256 = 97fc51ac2b085d4cde31ef4d2c3122c21abc217e9090a43a30fc5ec21684e059
[build.not(os=windows)]
builder = autoconf
subdir = ncurses-6.3
[autoconf.args]
--without-cxx-binding
--without-ada
[autoconf.args.os=linux]
--enable-shared
--with-shared
[build.os=windows]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/nghttp2
================================================
[manifest]
name = nghttp2
[rpms]
libnghttp2-devel
libnghttp2
[debs]
libnghttp2-dev
[pps]
libnghttp2
[download]
url = https://github.com/nghttp2/nghttp2/releases/download/v1.47.0/nghttp2-1.47.0.tar.gz
sha256 = 62f50f0e9fc479e48b34e1526df8dd2e94136de4c426b7680048181606832b7c
[build]
builder = autoconf
subdir = nghttp2-1.47.0
[autoconf.args]
--enable-lib-only
--disable-dependency-tracking
================================================
FILE: build/fbcode_builder/manifests/ninja
================================================
[manifest]
name = ninja
[debs]
ninja-build
[homebrew]
ninja
[rpms]
ninja-build
[pps]
ninja
[download.os=windows]
url = https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-win.zip
sha256 = f550fec705b6d6ff58f2db3c374c2277a37691678d6aba463adcbb129108467a
[build.os=windows]
builder = nop
[install.files.os=windows]
ninja.exe = bin/ninja.exe
[download.not(os=windows)]
url = https://github.com/ninja-build/ninja/archive/v1.12.1.tar.gz
sha256 = 821bdff48a3f683bc4bb3b6f0b5fe7b2d647cf65d52aeb63328c91a6c6df285a
[build.not(os=windows)]
builder = ninja_bootstrap
subdir = ninja-1.12.1
================================================
FILE: build/fbcode_builder/manifests/nlohmann-json
================================================
[manifest]
name = nlohmann-json
[download]
url = https://github.com/nlohmann/json/archive/refs/tags/v3.10.5.tar.gz
sha256 = 5daca6ca216495edf89d167f808d1d03c4a4d929cef7da5e10f135ae1540c7e4
[dependencies]
[build]
builder = cmake
subdir = json-3.10.5
================================================
FILE: build/fbcode_builder/manifests/nmap
================================================
[manifest]
name = nmap
[rpms]
nmap
nmap-ncat
[debs]
nmap
# 18.04 combines ncat into the nmap package, newer need the separate one
[debs.not(all(distro=ubuntu,distro_vers="18.04"))]
ncat
[download.not(os=windows)]
url = https://api.github.com/repos/nmap/nmap/tarball/ef8213a36c2e89233c806753a57b5cd473605408
sha256 = eda39e5a8ef4964fac7db16abf91cc11ff568eac0fa2d680b0bfa33b0ed71f4a
[build.not(os=windows)]
builder = autoconf
subdir = nmap-nmap-ef8213a
build_in_src_dir = true
[build.os=windows]
builder = nop
[autoconf.args]
# Without this option the build was filing to find some third party libraries
# that we don't need
enable_rdma=no
================================================
FILE: build/fbcode_builder/manifests/numa
================================================
[manifest]
name = numa
[download]
url = https://github.com/numactl/numactl/releases/download/v2.0.19/numactl-2.0.19.tar.gz
sha256 = f2672a0381cb59196e9c246bf8bcc43d5568bc457700a697f1a1df762b9af884
[build]
builder = autoconf
subdir = numactl-2.0.19
[rpms.distro=centos_stream]
numactl-devel
================================================
FILE: build/fbcode_builder/manifests/openr
================================================
[manifest]
name = openr
fbsource_path = facebook/openr
shipit_project = openr
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/openr.git
[build.os=linux]
builder = cmake
# openr files take a lot of RAM to compile.
job_weight_mib = 3072
[build.not(os=linux)]
# boost.fiber is required and that is not available on macos.
builder = nop
[dependencies]
boost
fb303
fbthrift
folly
googletest
re2
range-v3
[cmake.defines.test=on]
BUILD_TESTS=ON
ADD_ROOT_TESTS=OFF
[cmake.defines.test=off]
BUILD_TESTS=OFF
[shipit.pathmap]
fbcode/openr = openr
fbcode/openr/public_tld = .
================================================
FILE: build/fbcode_builder/manifests/openssl
================================================
[manifest]
name = openssl
[debs]
libssl-dev
[homebrew]
openssl
# on homebrew need the matching curl and ca-
[rpms]
openssl
openssl-devel
openssl-libs
[pps]
openssl
# no need to download on the systems where we always use the system libs
[download.not(any(os=linux, os=freebsd))]
# match the openssl version packages in ubuntu LTS folly current supports
url = https://www.openssl.org/source/openssl-3.0.15.tar.gz
sha256 = 23c666d0edf20f14249b3d8f0368acaee9ab585b09e1de82107c66e1f3ec9533
# We use the system openssl on these platforms even without --allow-system-packages
[build.any(os=linux, os=freebsd)]
builder = nop
[build.not(any(os=linux, os=freebsd))]
builder = openssl
subdir = openssl-3.0.15
[dependencies.os=windows]
jom
perl
================================================
FILE: build/fbcode_builder/manifests/osxfuse
================================================
[manifest]
name = osxfuse
[download]
url = https://github.com/osxfuse/osxfuse/archive/osxfuse-3.8.3.tar.gz
sha256 = 93bab6731bdfe8dc1ef069483437270ce7fe5a370f933d40d8d0ef09ba846c0c
[build]
builder = nop
[install.files]
osxfuse-osxfuse-3.8.3/common = include
================================================
FILE: build/fbcode_builder/manifests/patchelf
================================================
[manifest]
name = patchelf
[rpms]
patchelf
[debs]
patchelf
[pps]
patchelf
[download]
url = https://github.com/NixOS/patchelf/archive/0.10.tar.gz
sha256 = b3cb6bdedcef5607ce34a350cf0b182eb979f8f7bc31eae55a93a70a3f020d13
[build]
builder = autoconf
subdir = patchelf-0.10
================================================
FILE: build/fbcode_builder/manifests/pcre2
================================================
[manifest]
name = pcre2
[homebrew]
pcre2
[rpms]
pcre2-devel
pcre-static
[debs]
libpcre2-dev
[download]
url = https://github.com/PCRE2Project/pcre2/releases/download/pcre2-10.40/pcre2-10.40.tar.bz2
sha256 = 14e4b83c4783933dc17e964318e6324f7cae1bc75d8f3c79bc6969f00c159d68
[build]
builder = cmake
subdir = pcre2-10.40
================================================
FILE: build/fbcode_builder/manifests/perl
================================================
[manifest]
name = perl
[download.os=windows]
url = https://strawberryperl.com/download/5.28.1.1/strawberry-perl-5.28.1.1-64bit-portable.zip
sha256 = 935c95ba096fa11c4e1b5188732e3832d330a2a79e9882ab7ba8460ddbca810d
[build.os=windows]
builder = nop
subdir = perl
================================================
FILE: build/fbcode_builder/manifests/pexpect
================================================
[manifest]
name = pexpect
[download]
url = https://files.pythonhosted.org/packages/0e/3e/377007e3f36ec42f1b84ec322ee12141a9e10d808312e5738f52f80a232c/pexpect-4.7.0-py2.py3-none-any.whl
sha256 = 2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1
[build]
builder = python-wheel
[dependencies]
python-ptyprocess
================================================
FILE: build/fbcode_builder/manifests/proxygen
================================================
[manifest]
name = proxygen
fbsource_path = fbcode/proxygen
shipit_project = proxygen
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/proxygen.git
[build.os=windows]
builder = nop
[build]
builder = cmake
subdir = .
job_weight_mib = 3072
[cmake.defines.test=on]
BUILD_TESTS = ON
[cmake.defines.test=off]
BUILD_TESTS = OFF
[dependencies]
zlib
gperf
folly
fizz
wangle
mvfst
c-ares
[dependencies.test=on]
googletest
[shipit.pathmap]
fbcode/proxygen/public_tld = .
fbcode/proxygen = proxygen
================================================
FILE: build/fbcode_builder/manifests/python
================================================
[manifest]
name = python
[homebrew]
python@3.10
# sapling needs match statements with arrive in python 3.12 in centos 10
[rpms.not(all(distro=centos_stream,distro_vers=9))]
python3
python3-devel
# Centos Stream 9 default python is 3.9, sapling needs 3.10+
[rpms.all(distro=centos_stream,distro_vers=9)]
python3.12
python3.12-devel
# sapling needs match statements with arrive in python 3.10 in ubuntu 22.04
[debs.not(all(distro=ubuntu,any(distro_vers="18.04",distro_vers="20.04")))]
python3-all-dev
[pps]
python3
[download]
url = https://www.python.org/ftp/python/3.10.19/Python-3.10.19.tgz
sha256 = a078fb2d7a216071ebbe2e34b5f5355dd6b6e9b0cd1bacc4a41c63990c5a0eec
[build]
builder = autoconf
subdir = Python-3.10.19
[autoconf.args]
--enable-shared
--with-ensurepip=install
# python's pkg-config libffi detection is broken
# See https://bugs.python.org/issue34823 for clearest description
# and pending PR https://github.com/python/cpython/pull/20451
# The documented workaround requires an environment variable derived from
# pkg-config to be passed into its configure step
[autoconf.envcmd.LDFLAGS]
pkg-config
--libs-only-L
libffi
[dependencies]
libffi
# eden tests expect the python bz2 support
bz2
# eden tests expect the python curses support
ncurses
================================================
FILE: build/fbcode_builder/manifests/python-3_14
================================================
# This is primarily to support CinderX's CI, so it's not heavily configured.
[manifest]
name = python-3_14
[download]
url = https://github.com/python/cpython/archive/refs/tags/v3.14.3.tar.gz
sha256 = f229a232052ae318d2fc8eb0aca4a02d631e7e1a8790ef1f9b65e1632743a469
[build]
builder = autoconf
subdir = cpython-3.14.3
================================================
FILE: build/fbcode_builder/manifests/python-click
================================================
[manifest]
name = python-click
[download]
url = https://files.pythonhosted.org/packages/d2/3d/fa76db83bf75c4f8d338c2fd15c8d33fdd7ad23a9b5e57eb6c5de26b430e/click-7.1.2-py2.py3-none-any.whl
sha256 = dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc
[build]
builder = python-wheel
[rpms]
python3-click
[debs]
python3-click
================================================
FILE: build/fbcode_builder/manifests/python-filelock
================================================
[manifest]
name = python-filelock
[download]
url = https://files.pythonhosted.org/packages/31/24/ee722b92f23b9ebd87783e893a75352c048bbbc1f67dce0d63b58b46cb48/filelock-3.3.2-py3-none-any.whl
sha256 = bb2a1c717df74c48a2d00ed625e5a66f8572a3a30baacb7657add1d7bac4097b
[build]
builder = python-wheel
================================================
FILE: build/fbcode_builder/manifests/python-main
================================================
# This is primarily to support CinderX's CI, so it's not heavily configured.
[manifest]
name = python-main
fbsource_path = third-party/python/main/pristine
# We don't actually have a shipit project for python-main, but we use getdeps
# built-in shipit implementation which just needs a shipit.pathmap.
shipit_project = dummy-name
[git]
repo_url = https://github.com/python/cpython.git
[shipit.pathmap]
third-party/python/main/pristine = .
[build]
builder = autoconf
================================================
FILE: build/fbcode_builder/manifests/python-psutil
================================================
[manifest]
name = python-psutil
[download]
url = https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl
sha256 = 4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34
[build]
builder = python-wheel
================================================
FILE: build/fbcode_builder/manifests/python-ptyprocess
================================================
[manifest]
name = python-ptyprocess
[download]
url = https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess-0.6.0-py2.py3-none-any.whl
sha256 = d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f
[build]
builder = python-wheel
================================================
FILE: build/fbcode_builder/manifests/python-pyyaml
================================================
[manifest]
name = python-pyyaml
[download]
url = https://files.pythonhosted.org/packages/25/a2/b725b61ac76a75583ae7104b3209f75ea44b13cfd026aa535ece22b7f22e/PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl
sha256 = 22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6
[build]
builder = python-wheel
================================================
FILE: build/fbcode_builder/manifests/python-setuptools
================================================
[manifest]
name = python-setuptools
[download]
url = https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl
sha256 = 062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922
[build]
builder = python-wheel
[rpms]
python3-setuptools
# Centos Stream 9 default python is 3.9, sapling needs 3.10+
[rpms.all(distro=centos_stream,distro_vers=9)]
python3.12-setuptools
[homebrew]
python-setuptools
[debs]
python3-setuptools
================================================
FILE: build/fbcode_builder/manifests/python-setuptools-69
================================================
[manifest]
name = python-setuptools-69
[download]
url = https://files.pythonhosted.org/packages/c0/7a/3da654f49c95d0cc6e9549a855b5818e66a917e852ec608e77550c8dc08b/setuptools-69.1.1-py3-none-any.whl
sha256 = 02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56
[build]
builder = python-wheel
[rpms]
python3-setuptools
[homebrew]
python-setuptools
[debs]
python3-setuptools
================================================
FILE: build/fbcode_builder/manifests/python-six
================================================
[manifest]
name = python-six
[download]
url = https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl
sha256 = 3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c
[build]
builder = python-wheel
================================================
FILE: build/fbcode_builder/manifests/python-toml
================================================
[manifest]
name = python-toml
[download]
url = https://files.pythonhosted.org/packages/a2/12/ced7105d2de62fa7c8fb5fce92cc4ce66b57c95fb875e9318dba7f8c5db0/toml-0.10.0-py2.py3-none-any.whl
sha256 = 235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e
[build]
builder = python-wheel
================================================
FILE: build/fbcode_builder/manifests/ragel
================================================
[manifest]
name = ragel
[debs]
ragel
[homebrew]
ragel
[rpms]
ragel
[download]
url = https://www.colm.net/files/ragel/ragel-6.10.tar.gz
sha256 = 5f156edb65d20b856d638dd9ee2dfb43285914d9aa2b6ec779dac0270cd56c3f
[build]
builder = autoconf
subdir = ragel-6.10
================================================
FILE: build/fbcode_builder/manifests/range-v3
================================================
[manifest]
name = range-v3
[download]
url = https://github.com/ericniebler/range-v3/archive/refs/tags/0.11.0.tar.gz
sha256 = 376376615dbba43d3bef75aa590931431ecb49eb36d07bb726a19f680c75e20c
[build]
builder = cmake
subdir = range-v3-0.11.0
[cmake.defines]
RANGE_V3_EXAMPLES=OFF
================================================
FILE: build/fbcode_builder/manifests/rdma-core
================================================
[manifest]
name = rdma-core
[debs]
rdma-core
[rpms]
rdma-core-devel
================================================
FILE: build/fbcode_builder/manifests/re2
================================================
[manifest]
name = re2
[homebrew]
re2
[debs]
libre2-dev
[rpms]
re2
re2-devel
[pps]
re2
[download]
url = https://github.com/google/re2/archive/2020-11-01.tar.gz
sha256 = 8903cc66c9d34c72e2bc91722288ebc7e3ec37787ecfef44d204b2d6281954d7
[build]
builder = cmake
subdir = re2-2020-11-01
================================================
FILE: build/fbcode_builder/manifests/rebalancer
================================================
[manifest]
name = rebalancer
fbsource_path = fbcode/algopt/rebalancer/
shipit_project = rebalancer
shipit_fbcode_builder = true
use_shipit = true
[git]
# To git clone on devserver, setup fwdproxy:
# https://www.internalfb.com/wiki/Open_Source/Maintain_a_FB_OSS_Project/Devserver_GitHub_Access/
repo_url = https://github.com/facebookincubator/rebalancer.git
branch = main
[build]
builder = cmake
[dependencies]
boost
fbthrift
fizz
fmt
folly
gflags
glog
googletest
xxhash
[dependencies.os=linux]
clang19
[cmake.defines.os=linux]
CMAKE_C_COMPILER=clang-19
CMAKE_CXX_COMPILER=clang++-19
[cmake.defines.os=darwin]
REBALANCER_USE_SCIP=0
[shipit.strip]
^.*/fb/.*$
[shipit.pathmap]
fbcode/algopt/rebalancer/oss_root = .
fbcode/algopt/rebalancer = algopt/rebalancer
fbcode/algopt/lp = algopt/lp
[dependencies.all(distro=centos_stream,distro_vers=9)]
clang19
[cmake.defines.all(distro=centos_stream,distro_vers=9)]
CMAKE_C_COMPILER=clang-19
CMAKE_CXX_COMPILER=clang++-19
================================================
FILE: build/fbcode_builder/manifests/ripgrep
================================================
[manifest]
name = ripgrep
[rpms]
ripgrep
[debs]
ripgrep
[homebrew]
ripgrep
# only used from system packages currently
[build]
builder = nop
================================================
FILE: build/fbcode_builder/manifests/rocksdb
================================================
[manifest]
name = rocksdb
[download]
url = https://github.com/facebook/rocksdb/archive/refs/tags/v8.7.3.zip
sha256 = 36c06b61dc167f2455990d60dd88d734b73aa8c4dfc095243efd0243834c6cd3
[dependencies]
lz4
snappy
[build]
builder = cmake
subdir = rocksdb-8.7.3
[cmake.defines]
WITH_SNAPPY=ON
WITH_LZ4=ON
WITH_TESTS=OFF
WITH_BENCHMARK_TOOLS=OFF
# We get relocation errors with the static gflags lib,
# and there's no clear way to make it pick the shared gflags
# so just turn it off.
WITH_GFLAGS=OFF
# Disable the use of -Werror
FAIL_ON_WARNINGS = OFF
[cmake.defines.os=windows]
ROCKSDB_INSTALL_ON_WINDOWS=ON
# RocksDB hard codes the paths to the snappy libs to something
# that doesn't exist; ignoring the usual cmake rules. As a result,
# we can't build it with snappy without either patching rocksdb or
# without introducing more complex logic to the build system to
# connect the snappy build outputs to rocksdb's custom logic here.
# Let's just turn it off on windows.
WITH_SNAPPY=OFF
WITH_LZ4=ON
ROCKSDB_SKIP_THIRDPARTY=ON
================================================
FILE: build/fbcode_builder/manifests/rust-shed
================================================
[manifest]
name = rust-shed
fbsource_path = fbcode/common/rust/shed
shipit_project = rust-shed
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebookexperimental/rust-shed.git
[build]
builder = cargo
[cargo]
build_doc = true
workspace_dir =
[shipit.pathmap]
fbcode/common/rust/shed = shed
fbcode/common/rust/shed/public_autocargo = shed
fbcode/common/rust/shed/public_tld = .
tools/rust/ossconfigs = .
[shipit.strip]
^fbcode/common/rust/shed/(?!public_autocargo|public_tld).+/Cargo\.toml$
[dependencies]
fbthrift
fb303
# We use the system openssl on linux
[dependencies.not(os=linux)]
openssl
[dependencies.fbsource=on]
rust
================================================
FILE: build/fbcode_builder/manifests/sapling
================================================
[manifest]
name = sapling
fbsource_path = fbcode/eden
shipit_project = eden
shipit_fbcode_builder = true
[github.actions]
required_locales = en_US.UTF-8
[git]
repo_url = https://github.com/facebook/sapling.git
[build.not(os=windows)]
builder = make
subdir = eden/scm
[build.os=windows]
# For now the biggest blocker is missing "make" on windows, but there are bound
# to be more
builder = nop
[make.build_args]
getdepsbuild
[make.install_args]
install-getdeps
[make.test_args]
test-getdeps
[shipit.pathmap]
fbcode/configerator/structs/scm/hg = configerator/structs/scm/hg
fbcode/configerator/structs/scm/hg/public_autocargo = configerator/structs/scm/hg
fbcode/eden/oss = .
fbcode/eden = eden
fbcode/eden/fs/public_autocargo = eden/fs
fbcode/eden/mononoke/public_autocargo = eden/mononoke
fbcode/eden/scm/public_autocargo = eden/scm
fbcode/tools/lfs = tools/lfs
[shipit.strip]
^fbcode/configerator/structs/scm/hg(?!/public_autocargo).*/Cargo\.toml$
^fbcode/eden/addons/.*$
^fbcode/eden/fs/eden-config\.h$
^fbcode/eden/fs/py/eden/config\.py$
^fbcode/eden/hg-server/.*$
^fbcode/eden/fs(?!/public_autocargo).*/Cargo\.toml$
^fbcode/eden/mononoke(?!/public_autocargo).*/Cargo\.toml$
^fbcode/eden/scm(?!/public_autocargo|/edenscmnative/bindings).*/Cargo\.toml$
^fbcode/eden/scm/build/.*$
^fbcode/eden/website/.*$
^fbcode/eden/.*/\.cargo/.*$
^.*/facebook/.*$
^.*/fb/.*$
/Cargo\.lock$
\.pyc$
[dependencies]
fb303
fbthrift
rust-shed
[dependencies.all(test=on,not(os=darwin))]
hexdump
[dependencies.not(os=windows)]
python
python-setuptools
# We use the system openssl on linux
[dependencies.not(os=linux)]
openssl
[dependencies.fbsource=on]
rust
================================================
FILE: build/fbcode_builder/manifests/snappy
================================================
[manifest]
name = snappy
[homebrew]
snappy
[debs]
libsnappy-dev
[rpms]
snappy-devel
[pps]
snappy
[download]
url = https://github.com/google/snappy/archive/1.1.7.tar.gz
sha256 = 3dfa02e873ff51a11ee02b9ca391807f0c8ea0529a4924afa645fbf97163f9d4
[build]
builder = cmake
subdir = snappy-1.1.7
[cmake.defines]
SNAPPY_BUILD_TESTS = OFF
# Avoid problems like `relocation R_X86_64_PC32 against symbol` on ELF systems
# when linking rocksdb, which builds PIC even when building a static lib
[cmake.defines.os=linux]
BUILD_SHARED_LIBS = ON
================================================
FILE: build/fbcode_builder/manifests/sparsemap
================================================
[manifest]
name = sparsemap
[download]
url = https://github.com/Tessil/sparse-map/archive/refs/tags/v0.6.2.tar.gz
sha256 = 7020c21e8752e59d72e37456cd80000e18671c803890a3e55ae36b295eba99f6
[build]
builder = cmake
subdir = sparse-map-0.6.2/
================================================
FILE: build/fbcode_builder/manifests/sqlite3
================================================
[manifest]
name = sqlite3
[debs]
libsqlite3-dev
sqlite3
[homebrew]
sqlite
[rpms]
sqlite-devel
sqlite-libs
sqlite
[pps]
sqlite3
[download]
url = https://sqlite.org/2019/sqlite-amalgamation-3280000.zip
sha256 = d02fc4e95cfef672b45052e221617a050b7f2e20103661cda88387349a9b1327
[dependencies]
cmake
ninja
[build]
builder = sqlite
subdir = sqlite-amalgamation-3280000
================================================
FILE: build/fbcode_builder/manifests/systemd
================================================
[manifest]
name = systemd
[rpms]
systemd
systemd-devel
[download]
url = https://github.com/systemd/systemd/archive/refs/tags/v256.7.tar.gz
sha256 = 896d76ff65c88f5fd9e42f90d152b0579049158a163431dd77cdc57748b1d7b0
[build.os=linux]
builder = meson
subdir = systemd-256.7
[meson.setup_args]
-Dstatic-libsystemd=true
-Dprefix=/
================================================
FILE: build/fbcode_builder/manifests/tabulate
================================================
[manifest]
name = tabulate
[download]
url = https://github.com/p-ranav/tabulate/archive/refs/tags/v1.5.tar.gz
sha256 = 16b289f46306283544bb593f4601e80d6ea51248fde52e910cc569ef08eba3fb
[build]
builder = cmake
subdir = tabulate-1.5
[cmake.defines]
tabulate_BUILD_TESTS = OFF
tabulate_BUILD_SAMPLES = OFF
================================================
FILE: build/fbcode_builder/manifests/tree
================================================
[manifest]
name = tree
[debs]
tree
[homebrew]
tree
[rpms]
tree
[download.os=linux]
url = https://salsa.debian.org/debian/tree-packaging/-/archive/debian/1.8.0-1/tree-packaging-debian-1.8.0-1.tar.gz
sha256 = a841eee1d52bfd64a48f54caab9937b9bd92935055c48885c4ab1ae4dab7fae5
[download.os=darwin]
# The official package of tree source requires users of non-Linux platform to
# comment/uncomment certain lines in the Makefile to build for their platform.
# Besauce getdeps.py doesn't have that functionality we just use this custom
# fork of tree which has proper lines uncommented for a OSX build
url = https://github.com/lukaspiatkowski/tree-command/archive/debian/1.8.0-1-macos.tar.gz
sha256 = 9cbe889553d95cf5a2791dd0743795d46a3c092c5bba691769c0e5c52e11229e
[build.os=linux]
builder = make
subdir = tree-packaging-debian-1.8.0-1
[build.os=darwin]
builder = make
subdir = tree-command-debian-1.8.0-1-macos
[build.os=windows]
builder = nop
[make.install_args]
install
================================================
FILE: build/fbcode_builder/manifests/wangle
================================================
[manifest]
name = wangle
fbsource_path = fbcode/wangle
shipit_project = wangle
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/wangle.git
[build]
builder = cmake
subdir = wangle
[cmake.defines.test=on]
BUILD_TESTS=ON
[cmake.defines.test=off]
BUILD_TESTS=OFF
[dependencies]
folly
googletest
fizz
[shipit.pathmap]
fbcode/wangle/public_tld = .
fbcode/wangle = wangle
================================================
FILE: build/fbcode_builder/manifests/wangle-python
================================================
[manifest]
name = wangle-python
fbsource_path = fbcode/wangle
shipit_project = wangle
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/wangle.git
[build]
builder = cmake
subdir = wangle
[build.not(os=linux)]
builder = nop
[cmake.defines.test=on]
BUILD_TESTS=ON
[cmake.defines.test=off]
BUILD_TESTS=OFF
[cmake.defines.os=linux]
CMAKE_POSITION_INDEPENDENT_CODE = ON
BUILD_SHARED_LIBS = ON
[dependencies]
folly-python
googletest
fizz-python
[shipit.pathmap]
fbcode/wangle/public_tld = .
fbcode/wangle = wangle
================================================
FILE: build/fbcode_builder/manifests/watchman
================================================
[manifest]
name = watchman
fbsource_path = fbcode/watchman
shipit_project = watchman
shipit_fbcode_builder = true
[git]
repo_url = https://github.com/facebook/watchman.git
[build]
builder = cmake
[dependencies]
boost
cpptoml
edencommon
fb303
fbthrift
folly
pcre2
googletest
python-setuptools-69
[dependencies.fbsource=on]
rust
[shipit.pathmap]
fbcode/watchman = watchman
fbcode/watchman/oss = .
fbcode/eden/fs = eden/fs
[shipit.strip]
^fbcode/eden/fs/(?!.*\.thrift|service/shipit_test_file\.txt)
[cmake.defines.fb=on]
ENABLE_EDEN_SUPPORT=ON
IS_FB_BUILD=ON
# FB macos specific settings
[cmake.defines.all(fb=on,os=darwin)]
# this path is coupled with the FB internal watchman-osx.spec
WATCHMAN_STATE_DIR=/opt/facebook/watchman/var/run/watchman
# tell cmake not to try to create /opt/facebook/...
INSTALL_WATCHMAN_STATE_DIR=OFF
USE_SYS_PYTHON=OFF
[depends.environment]
WATCHMAN_VERSION_OVERRIDE
================================================
FILE: build/fbcode_builder/manifests/xxhash
================================================
[manifest]
name = xxhash
[download]
url = https://github.com/Cyan4973/xxHash/archive/refs/tags/v0.8.2.tar.gz
sha256 = baee0c6afd4f03165de7a4e67988d16f0f2b257b51d0e3cb91909302a26a79c4
[rpms]
xxhash-devel
[debs]
libxxhash-dev
xxhash
[homebrew]
xxhash
[build]
builder = cmake
subdir = xxHash-0.8.2/cmake_unofficial
[cmake.defines]
CMAKE_POSITION_INDEPENDENT_CODE = ON
================================================
FILE: build/fbcode_builder/manifests/xz
================================================
[manifest]
name = xz
# ubuntu's package causes watchman's tests to hang
[debs.not(distro=ubuntu)]
liblzma-dev
[homebrew]
xz
[rpms]
xz-devel
[download]
url = https://tukaani.org/xz/xz-5.2.5.tar.gz
sha256 = f6f4910fd033078738bd82bfba4f49219d03b17eb0794eb91efbae419f4aba10
[build]
builder = autoconf
subdir = xz-5.2.5
[autoconf.args]
--with-pic
================================================
FILE: build/fbcode_builder/manifests/yaml-cpp
================================================
[manifest]
name = yaml-cpp
[download]
url = https://github.com/jbeder/yaml-cpp/archive/yaml-cpp-0.6.2.tar.gz
sha256 = e4d8560e163c3d875fd5d9e5542b5fd5bec810febdcba61481fe5fc4e6b1fd05
[build.os=linux]
builder = cmake
subdir = yaml-cpp-yaml-cpp-0.6.2
[build.not(os=linux)]
builder = nop
[dependencies]
boost
googletest
[cmake.defines]
YAML_CPP_BUILD_TESTS=OFF
================================================
FILE: build/fbcode_builder/manifests/zlib
================================================
[manifest]
name = zlib
[debs]
zlib1g-dev
[homebrew]
zlib
[rpms.not(distro=fedora)]
zlib-devel
zlib-static
[rpms.distro=fedora]
zlib-ng-compat-devel
zlib-ng-compat-static
[pps]
zlib
[download]
url = https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz
sha256 = 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
[build]
builder = cmake
subdir = zlib-1.3.1
patchfile = zlib_dont_build_more_than_needed.patch
================================================
FILE: build/fbcode_builder/manifests/zlib-python
================================================
[manifest]
name = zlib-python
[debs]
zlib1g-dev
[homebrew]
zlib
[rpms.not(distro=fedora)]
zlib-devel
zlib-static
[rpms.distro=fedora]
zlib-ng-compat-devel
zlib-ng-compat-static
[pps]
zlib
[download]
url = https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz
sha256 = 9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23
[build]
builder = cmake
subdir = zlib-1.3.1
patchfile = zlib_dont_build_more_than_needed.patch
[build.not(os=linux)]
builder = nop
[cmake.defines]
CMAKE_POSITION_INDEPENDENT_CODE=ON
================================================
FILE: build/fbcode_builder/manifests/zstd
================================================
[manifest]
name = zstd
[homebrew]
zstd
# 18.04 zstd is too old
[debs.not(all(distro=ubuntu,distro_vers="18.04"))]
libclang-dev
libzstd-dev
zstd
[rpms]
libzstd-devel
libzstd
[pps]
zstd
[download]
url = https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-1.5.5.tar.gz
sha256 = 9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4
[build]
builder = cmake
subdir = zstd-1.5.5/build/cmake
# The zstd cmake build explicitly sets the install name
# for the shared library in such a way that cmake discards
# the path to the library from the install_name, rendering
# the library non-resolvable during the build. The short
# term solution for this is just to link static on macos.
#
# And while we're at it, let's just always link statically.
[cmake.defines]
ZSTD_BUILD_SHARED = OFF
================================================
FILE: build/fbcode_builder/manifests/zstd-python
================================================
[manifest]
name = zstd-python
[homebrew]
zstd
# 18.04 zstd is too old
[debs.not(all(distro=ubuntu,distro_vers="18.04"))]
libclang-dev
libzstd-dev
zstd
[rpms]
libzstd-devel
libzstd
[pps]
zstd
[download]
url = https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-1.5.5.tar.gz
sha256 = 9c4396cc829cfae319a6e2615202e82aad41372073482fce286fac78646d3ee4
[build]
builder = cmake
subdir = zstd-1.5.5/build/cmake
[build.not(os=linux)]
builder = nop
# The zstd cmake build explicitly sets the install name
# for the shared library in such a way that cmake discards
# the path to the library from the install_name, rendering
# the library non-resolvable during the build. The short
# term solution for this is just to link static on macos.
#
# And while we're at it, let's just always link statically.
[cmake.defines]
ZSTD_BUILD_SHARED = OFF
CMAKE_POSITION_INDEPENDENT_CODE=ON
================================================
FILE: build/fbcode_builder/patches/boost_1_83_0.patch
================================================
diff --git a/boost/serialization/strong_typedef.hpp b/boost/serialization/strong_typedef.hpp
--- a/boost/serialization/strong_typedef.hpp
+++ b/boost/serialization/strong_typedef.hpp
@@ -44,6 +44,7 @@
operator const T&() const {return t;} \
operator T&() {return t;} \
bool operator==(const D& rhs) const {return t == rhs.t;} \
+ bool operator==(const T& lhs) const {return t == lhs;} \
bool operator<(const D& rhs) const {return t < rhs.t;} \
};
diff --git a/tools/build/src/tools/msvc.jam b/tools/build/src/tools/msvc.jam
--- a/tools/build/src/tools/msvc.jam
+++ b/tools/build/src/tools/msvc.jam
@@ -1137,6 +1137,14 @@
}
else
{
+ if [ MATCH "(14.4)" : $(version) ]
+ {
+ if $(.debug-configuration)
+ {
+ ECHO "notice: [generate-setup-cmd] $(version) is 14.4x" ;
+ }
+ parent = [ path.native [ path.join $(parent) "..\\..\\..\\..\\..\\Auxiliary\\Build" ] ] ;
+ }
if [ MATCH "(14.3)" : $(version) ]
{
if $(.debug-configuration)
================================================
FILE: build/fbcode_builder/patches/iproute2_oss.patch
================================================
diff --git a/bridge/fdb.c b/bridge/fdb.c
--- a/bridge/fdb.c
+++ b/bridge/fdb.c
@@ -31,7 +31,7 @@
static unsigned int filter_index, filter_vlan, filter_state;
-json_writer_t *jw_global;
+static json_writer_t *jw_global;
static void usage(void)
{
diff --git a/ip/ipmroute.c b/ip/ipmroute.c
--- a/ip/ipmroute.c
+++ b/ip/ipmroute.c
@@ -44,7 +44,7 @@
exit(-1);
}
-struct rtfilter {
+static struct rtfilter {
int tb;
int af;
int iif;
diff --git a/ip/xfrm_monitor.c b/ip/xfrm_monitor.c
--- a/ip/xfrm_monitor.c
+++ b/ip/xfrm_monitor.c
@@ -34,7 +34,7 @@
#include "ip_common.h"
static void usage(void) __attribute__((noreturn));
-int listen_all_nsid;
+static int listen_all_nsid;
static void usage(void)
{
================================================
FILE: build/fbcode_builder/patches/libiberty_install_pic_lib.patch
================================================
diff --git a/Makefile.in b/Makefile.in
index b77a41c..cbe71fe 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -389,7 +389,7 @@ MULTIOSDIR = `$(CC) $(CFLAGS) -print-multi-os-directory`
install_to_libdir: all
if test -n "${target_header_dir}"; then \
${mkinstalldirs} $(DESTDIR)$(libdir)/$(MULTIOSDIR); \
- $(INSTALL_DATA) $(TARGETLIB) $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB)n; \
+ $(INSTALL_DATA) pic/$(TARGETLIB) $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB)n; \
( cd $(DESTDIR)$(libdir)/$(MULTIOSDIR) ; chmod 644 $(TARGETLIB)n ;$(RANLIB) $(TARGETLIB)n ); \
mv -f $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB)n $(DESTDIR)$(libdir)/$(MULTIOSDIR)/$(TARGETLIB); \
case "${target_header_dir}" in \
================================================
FILE: build/fbcode_builder/patches/zlib_dont_build_more_than_needed.patch
================================================
diff -Naur ../zlib-1.3.1/CMakeLists.txt ./CMakeLists.txt
--- ../zlib-1.3.1/CMakeLists.txt 2024-01-22 10:32:37.000000000 -0800
+++ ./CMakeLists.txt 2024-01-23 13:14:09.870289968 -0800
@@ -149,10 +149,8 @@
set(ZLIB_DLL_SRCS ${CMAKE_CURRENT_BINARY_DIR}/zlib1rc.obj)
endif(MINGW)
-add_library(zlib SHARED ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
+add_library(zlib ${ZLIB_SRCS} ${ZLIB_DLL_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
target_include_directories(zlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
-add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})
-target_include_directories(zlibstatic PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(zlib PROPERTIES DEFINE_SYMBOL ZLIB_DLL)
set_target_properties(zlib PROPERTIES SOVERSION 1)
@@ -169,7 +167,7 @@
if(UNIX)
# On unix-like platforms the library is almost always called libz
- set_target_properties(zlib zlibstatic PROPERTIES OUTPUT_NAME z)
+ set_target_properties(zlib PROPERTIES OUTPUT_NAME z)
if(NOT APPLE AND NOT(CMAKE_SYSTEM_NAME STREQUAL AIX))
set_target_properties(zlib PROPERTIES LINK_FLAGS "-Wl,--version-script,\"${CMAKE_CURRENT_SOURCE_DIR}/zlib.map\"")
endif()
@@ -179,7 +177,7 @@
endif()
if(NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL )
- install(TARGETS zlib zlibstatic
+ install(TARGETS zlib
RUNTIME DESTINATION "${INSTALL_BIN_DIR}"
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}"
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" )
================================================
FILE: build.bat
================================================
@REM Copyright (c) Meta Platforms, Inc. and affiliates.
@REM
@REM Licensed under the Apache License, Version 2.0 (the "License");
@REM you may not use this file except in compliance with the License.
@REM You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing, software
@REM distributed under the License is distributed on an "AS IS" BASIS,
@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@REM See the License for the specific language governing permissions and
@REM limitations under the License.
@echo off
SETLOCAL
if exist %~dp0\..\..\opensource\fbcode_builder\getdeps.py (
set GETDEPS=%~dp0\..\..\opensource\fbcode_builder\getdeps.py
) else if exist %~dp0\build\fbcode_builder\getdeps.py (
set GETDEPS=%~dp0\build\fbcode_builder\getdeps.py
) else (
echo "error: unable to find getdeps.py"
exit 1
)
python3.exe %GETDEPS% build folly %*
================================================
FILE: build.sh
================================================
#!/bin/bash
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script is just a simple wrapper around the
# build/fbcode_builder/getdeps.py script.
#
# Feel free to invoke getdeps.py directly to have more control over the build.
SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}")
GETDEPS_PATHS=(
"$SCRIPT_DIR/build/fbcode_builder/getdeps.py"
"$SCRIPT_DIR/../../opensource/fbcode_builder/getdeps.py"
)
for getdeps in "${GETDEPS_PATHS[@]}"; do
if [[ -x "$getdeps" ]]; then
exec "$getdeps" build folly "$@"
fi
done
echo "Could not find getdeps.py" >&2
exit 1
================================================
FILE: folly/.clang-format
================================================
# The canary clang-format config file.
#
# Changes from head:
# - AllowAllParametersOfDeclarationOnNextLine: Safe
# - AllowShortFunctionsOnASingleLine: Safe
# - IncludeIsMainRegex: May cause errors, as it may reorder includes.
# - IncludeCategories: May cause errors, as it may reorder includes.
---
AccessModifierOffset: -1
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Left
AlignOperands: DontAlign
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
FixNamespaceComments: true
ForEachMacros:
- FOR_EACH
- FOR_EACH_R
- FOR_EACH_RANGE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<(sodium|Python)\.h>$'
Priority: 3
- Regex: '^<((arpa|net|netinet|sys|linux)\/)?[-_[:alnum:]]+\.h>$'
Priority: 1
- Regex: '^<[\/-_[:alnum:]]+>$'
Priority: 2
- Regex: '^<((bistro|caffe2|cachelib|fatal|fb303|faiss|fbmeshd|fboss|fbzmq|fizz|folly|gloo|hphp|logdevice|magma|mcrouter|openr|proxygen|quic|rsocket|scape|squangle|tensorpipe|thrift|wangle|wdt|yarpl)\/).*>$'
Priority: 4
- Regex: '^<.*>$'
Priority: 3
- Regex: '.*'
Priority: 5
IncludeIsMainRegex: '((Test|Tests|_test|Bench|Benchmark|Performance)[0-9]*)?$'
IndentCaseLabels: true
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 10
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
TabWidth: 8
UseCRLF: false
UseTab: Never
...
================================================
FILE: folly/AtomicHashArray-inl.h
================================================
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FOLLY_ATOMICHASHARRAY_H_
#error "This should only be included by AtomicHashArray.h"
#endif
#include
#include
#include
#include
#include
namespace folly {
// AtomicHashArray private constructor --
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::
AtomicHashArray(
size_t capacity,
KeyT emptyKey,
KeyT lockedKey,
KeyT erasedKey,
double _maxLoadFactor,
uint32_t cacheSize)
: capacity_(capacity),
maxEntries_(size_t(_maxLoadFactor * capacity_ + 0.5)),
kEmptyKey_(emptyKey),
kLockedKey_(lockedKey),
kErasedKey_(erasedKey),
kAnchorMask_(nextPowTwo(capacity_) - 1),
numEntries_(0, cacheSize),
numPendingEntries_(0, cacheSize),
isFull_(0),
numErases_(0) {
if (capacity == 0) {
throw_exception("capacity");
}
}
/*
* findInternal --
*
* Sets ret.second to value found and ret.index to index
* of key and returns true, or if key does not exist returns false and
* ret.index is set to capacity_.
*/
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
template
typename AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::SimpleRetT
AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::findInternal(const LookupKeyT key_in) {
checkLegalKeyIfKey(key_in);
for (size_t idx = keyToAnchorIdx(key_in),
numProbes = 0;
;
idx = ProbeFcn()(idx, numProbes, capacity_)) {
const KeyT key = acquireLoadKey(cells_[idx]);
if (FOLLY_LIKELY(LookupEqualFcn()(key, key_in))) {
return SimpleRetT(idx, true);
}
if (FOLLY_UNLIKELY(key == kEmptyKey_)) {
// if we hit an empty element, this key does not exist
return SimpleRetT(capacity_, false);
}
// NOTE: the way we count numProbes must be same in find(), insert(),
// and erase(). Otherwise it may break probing.
++numProbes;
if (FOLLY_UNLIKELY(numProbes >= capacity_)) {
// probed every cell...fail
return SimpleRetT(capacity_, false);
}
}
}
/*
* insertInternal --
*
* Returns false on failure due to key collision or full.
* Also sets ret.index to the index of the key. If the map is full, sets
* ret.index = capacity_. Also sets ret.second to cell value, thus if insert
* successful this will be what we just inserted, if there is a key collision
* this will be the previously inserted value, and if the map is full it is
* default.
*/
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
template <
typename LookupKeyT,
typename LookupHashFcn,
typename LookupEqualFcn,
typename LookupKeyToKeyFcn,
typename... ArgTs>
typename AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::SimpleRetT
AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::insertInternal(LookupKeyT key_in, ArgTs&&... vCtorArgs) {
const short NO_NEW_INSERTS = 1;
const short NO_PENDING_INSERTS = 2;
checkLegalKeyIfKey(key_in);
size_t idx = keyToAnchorIdx(key_in);
size_t numProbes = 0;
for (;;) {
DCHECK_LT(idx, capacity_);
value_type* cell = &cells_[idx];
if (relaxedLoadKey(*cell) == kEmptyKey_) {
// NOTE: isFull_ is set based on numEntries_.readFast(), so it's
// possible to insert more than maxEntries_ entries. However, it's not
// possible to insert past capacity_.
++numPendingEntries_;
if (isFull_.load(std::memory_order_acquire)) {
--numPendingEntries_;
// Before deciding whether this insert succeeded, this thread needs to
// wait until no other thread can add a new entry.
// Correctness assumes isFull_ is true at this point. If
// another thread now does ++numPendingEntries_, we expect it
// to pass the isFull_.load() test above. (It shouldn't insert
// a new entry.)
detail::atomic_hash_spin_wait([&] {
return (isFull_.load(std::memory_order_acquire) !=
NO_PENDING_INSERTS) &&
(numPendingEntries_.readFull() != 0);
});
isFull_.store(NO_PENDING_INSERTS, std::memory_order_release);
if (relaxedLoadKey(*cell) == kEmptyKey_) {
// Don't insert past max load factor
return SimpleRetT(capacity_, false);
}
} else {
// An unallocated cell. Try once to lock it. If we succeed, insert here.
// If we fail, fall through to comparison below; maybe the insert that
// just beat us was for this very key....
if (tryLockCell(cell)) {
KeyT key_new;
// Write the value - done before unlocking
try {
key_new = LookupKeyToKeyFcn()(key_in);
typedef
typename std::remove_const::type LookupKeyTNoConst;
constexpr bool kAlreadyChecked =
std::is_same::value;
if (!kAlreadyChecked) {
checkLegalKeyIfKey(key_new);
}
DCHECK(relaxedLoadKey(*cell) == kLockedKey_);
// A const mapped_type is only constant once constructed, so cast
// away any const for the placement new here.
using mapped = typename std::remove_const::type;
new (const_cast(&cell->second))
ValueT(std::forward(vCtorArgs)...);
unlockCell(cell, key_new); // Sets the new key
} catch (...) {
// Transition back to empty key---requires handling
// locked->empty below.
unlockCell(cell, kEmptyKey_);
--numPendingEntries_;
throw;
}
// An erase() can race here and delete right after our insertion
// Direct comparison rather than EqualFcn ok here
// (we just inserted it)
DCHECK(
relaxedLoadKey(*cell) == key_new ||
relaxedLoadKey(*cell) == kErasedKey_);
--numPendingEntries_;
++numEntries_; // This is a thread cached atomic increment :)
if (numEntries_.readFast() >= maxEntries_) {
isFull_.store(NO_NEW_INSERTS, std::memory_order_relaxed);
}
return SimpleRetT(idx, true);
}
--numPendingEntries_;
}
}
DCHECK(relaxedLoadKey(*cell) != kEmptyKey_);
if (kLockedKey_ == acquireLoadKey(*cell)) {
detail::atomic_hash_spin_wait([&] {
return kLockedKey_ == acquireLoadKey(*cell);
});
}
const KeyT thisKey = acquireLoadKey(*cell);
if (LookupEqualFcn()(thisKey, key_in)) {
// Found an existing entry for our key, but we don't overwrite the
// previous value.
return SimpleRetT(idx, false);
} else if (thisKey == kEmptyKey_ || thisKey == kLockedKey_) {
// We need to try again (i.e., don't increment numProbes or
// advance idx): this case can happen if the constructor for
// ValueT threw for this very cell (the rethrow block above).
continue;
}
// NOTE: the way we count numProbes must be same in find(),
// insert(), and erase(). Otherwise it may break probing.
++numProbes;
if (FOLLY_UNLIKELY(numProbes >= capacity_)) {
// probed every cell...fail
return SimpleRetT(capacity_, false);
}
idx = ProbeFcn()(idx, numProbes, capacity_);
}
}
/*
* erase --
*
* This will attempt to erase the given key key_in if the key is found. It
* returns 1 iff the key was located and marked as erased, and 0 otherwise.
*
* Memory is not freed or reclaimed by erase, i.e. the cell containing the
* erased key will never be reused. If there's an associated value, we won't
* touch it either.
*/
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
size_t AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::erase(KeyT key_in) {
CHECK_NE(key_in, kEmptyKey_);
CHECK_NE(key_in, kLockedKey_);
CHECK_NE(key_in, kErasedKey_);
for (size_t idx = keyToAnchorIdx(key_in), numProbes = 0;;
idx = ProbeFcn()(idx, numProbes, capacity_)) {
DCHECK_LT(idx, capacity_);
value_type* cell = &cells_[idx];
KeyT currentKey = acquireLoadKey(*cell);
if (currentKey == kEmptyKey_ || currentKey == kLockedKey_) {
// If we hit an empty (or locked) element, this key does not exist. This
// is similar to how it's handled in find().
return 0;
}
if (EqualFcn()(currentKey, key_in)) {
// Found an existing entry for our key, attempt to mark it erased.
// Some other thread may have erased our key, but this is ok.
KeyT expect = currentKey;
if (cellKeyPtr(*cell)->compare_exchange_strong(expect, kErasedKey_)) {
numErases_.fetch_add(1, std::memory_order_relaxed);
// Even if there's a value in the cell, we won't delete (or even
// default construct) it because some other thread may be accessing it.
// Locking it meanwhile won't work either since another thread may be
// holding a pointer to it.
// We found the key and successfully erased it.
return 1;
}
// If another thread succeeds in erasing our key, we'll stop our search.
return 0;
}
// NOTE: the way we count numProbes must be same in find(), insert(),
// and erase(). Otherwise it may break probing.
++numProbes;
if (FOLLY_UNLIKELY(numProbes >= capacity_)) {
// probed every cell...fail
return 0;
}
}
}
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
typename AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::SmartPtr
AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::create(size_t maxSize, const Config& c) {
CHECK_LE(c.maxLoadFactor, 1.0);
CHECK_GT(c.maxLoadFactor, 0.0);
CHECK_NE(c.emptyKey, c.lockedKey);
size_t capacity = size_t(maxSize / c.maxLoadFactor);
size_t sz = sizeof(AtomicHashArray) + sizeof(value_type) * capacity;
auto const mem = Allocator().allocate(sz);
try {
new (mem) AtomicHashArray(
capacity,
c.emptyKey,
c.lockedKey,
c.erasedKey,
c.maxLoadFactor,
c.entryCountThreadCacheSize);
} catch (...) {
Allocator().deallocate(mem, sz);
throw;
}
SmartPtr map(static_cast((void*)mem));
/*
* Mark all cells as empty.
*
* Note: we're bending the rules a little here accessing the key
* element in our cells even though the cell object has not been
* constructed, and casting them to atomic objects (see cellKeyPtr).
* (Also, in fact we never actually invoke the value_type
* constructor.) This is in order to avoid needing to default
* construct a bunch of value_type when we first start up: if you
* have an expensive default constructor for the value type this can
* noticeably speed construction time for an AHA.
*/
FOR_EACH_RANGE (i, 0, map->capacity_) {
cellKeyPtr(map->cells_[i])
->store(map->kEmptyKey_, std::memory_order_relaxed);
}
return map;
}
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
void AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::destroy(AtomicHashArray* p) {
assert(p);
size_t sz = sizeof(AtomicHashArray) + sizeof(value_type) * p->capacity_;
FOR_EACH_RANGE (i, 0, p->capacity_) {
if (p->cells_[i].first != p->kEmptyKey_) {
p->cells_[i].~value_type();
}
}
p->~AtomicHashArray();
Allocator().deallocate((char*)p, sz);
}
// clear -- clears all keys and values in the map and resets all counters
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
void AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::clear() {
FOR_EACH_RANGE (i, 0, capacity_) {
if (cells_[i].first != kEmptyKey_) {
cells_[i].~value_type();
*const_cast(&cells_[i].first) = kEmptyKey_;
}
CHECK(cells_[i].first == kEmptyKey_);
}
numEntries_.set(0);
numPendingEntries_.set(0);
isFull_.store(0, std::memory_order_relaxed);
numErases_.store(0, std::memory_order_relaxed);
}
// Iterator implementation
template <
class KeyT,
class ValueT,
class HashFcn,
class EqualFcn,
class Allocator,
class ProbeFcn,
class KeyConvertFcn>
template
struct AtomicHashArray<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn,
KeyConvertFcn>::aha_iterator
: detail::IteratorFacade<
aha_iterator,
IterVal,
std::forward_iterator_tag> {
explicit aha_iterator() : aha_(nullptr) {}
// Conversion ctor for interoperability between const_iterator and
// iterator. The enable_if<> magic keeps us well-behaved for
// is_convertible<> (v. the iterator_facade documentation).
template
aha_iterator(
const aha_iterator& o,
typename std::enable_if<
std::is_convertible::value>::type* = nullptr)
: aha_(o.aha_), offset_(o.offset_) {}
explicit aha_iterator(ContT* array, size_t offset)
: aha_(array), offset_(offset) {}
// Returns unique index that can be used with findAt().
// WARNING: The following function will fail silently for hashtable
// with capacity > 2^32
uint32_t getIndex() const { return offset_; }
void advancePastEmpty() {
while (offset_ < aha_->capacity_ && !isValid()) {
++offset_;
}
}
private:
friend class AtomicHashArray;
friend class detail::
IteratorFacade;
void increment() {
++offset_;
advancePastEmpty();
}
bool equal(const aha_iterator& o) const {
return aha_ == o.aha_ && offset_ == o.offset_;
}
IterVal& dereference() const { return aha_->cells_[offset_]; }
bool isValid() const {
KeyT key = acquireLoadKey(aha_->cells_[offset_]);
return key != aha_->kEmptyKey_ && key != aha_->kLockedKey_ &&
key != aha_->kErasedKey_;
}
private:
ContT* aha_;
size_t offset_;
}; // aha_iterator
} // namespace folly
================================================
FILE: folly/AtomicHashArray.h
================================================
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* AtomicHashArray is the building block for AtomicHashMap. It provides the
* core lock-free functionality, but is limited by the fact that it cannot
* grow past its initialization size and is a little more awkward (no public
* constructor, for example). If you're confident that you won't run out of
* space, don't mind the awkwardness, and really need bare-metal performance,
* feel free to use AHA directly.
*
* Check out AtomicHashMap.h for more thorough documentation on perf and
* general pros and cons relative to other hash maps.
*
*/
#pragma once
#define FOLLY_ATOMICHASHARRAY_H_
#include
#include
#include
#include
namespace folly {
struct AtomicHashArrayLinearProbeFcn {
inline size_t operator()(
size_t idx, size_t /* numProbes */, size_t capacity) const {
idx += 1; // linear probing
// Avoid modulus because it's slow
return FOLLY_LIKELY(idx < capacity) ? idx : (idx - capacity);
}
};
struct AtomicHashArrayQuadraticProbeFcn {
inline size_t operator()(
size_t idx, size_t numProbes, size_t capacity) const {
idx += numProbes; // quadratic probing
// Avoid modulus because it's slow
return FOLLY_LIKELY(idx < capacity) ? idx : (idx - capacity);
}
};
// Enables specializing checkLegalKey without specializing its class.
namespace detail {
template
inline void checkLegalKeyIfKeyTImpl(
NotKeyT /* ignored */,
KeyT /* emptyKey */,
KeyT /* lockedKey */,
KeyT /* erasedKey */) {}
template
inline void checkLegalKeyIfKeyTImpl(
KeyT key_in, KeyT emptyKey, KeyT lockedKey, KeyT erasedKey) {
DCHECK_NE(key_in, emptyKey);
DCHECK_NE(key_in, lockedKey);
DCHECK_NE(key_in, erasedKey);
}
} // namespace detail
template <
class KeyT,
class ValueT,
class HashFcn = std::hash,
class EqualFcn = std::equal_to,
class Allocator = std::allocator,
class ProbeFcn = AtomicHashArrayLinearProbeFcn,
class KeyConvertFcn = Identity>
class AtomicHashMap;
template <
class KeyT,
class ValueT,
class HashFcn = std::hash,
class EqualFcn = std::equal_to,
class Allocator = std::allocator,
class ProbeFcn = AtomicHashArrayLinearProbeFcn,
class KeyConvertFcn = Identity>
class AtomicHashArray {
static_assert(
(std::is_convertible::value ||
std::is_convertible::value ||
std::is_convertible::value),
"You are trying to use AtomicHashArray with disallowed key "
"types. You must use atomically compare-and-swappable integer "
"keys, or a different container class.");
public:
using key_type = KeyT;
using mapped_type = ValueT;
using hasher = HashFcn;
using key_equal = EqualFcn;
using key_convert = KeyConvertFcn;
using value_type = std::pair;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
const size_t capacity_;
const size_t maxEntries_;
const KeyT kEmptyKey_;
const KeyT kLockedKey_;
const KeyT kErasedKey_;
template
struct aha_iterator;
using const_iterator = aha_iterator;
using iterator = aha_iterator;
// You really shouldn't need this if you use the SmartPtr provided by create,
// but if you really want to do something crazy like stick the released
// pointer into a DiscriminatedPtr or something, you'll need this to clean up
// after yourself.
static void destroy(AtomicHashArray*);
private:
const size_t kAnchorMask_;
struct Deleter {
void operator()(AtomicHashArray* ptr) { AtomicHashArray::destroy(ptr); }
};
public:
using SmartPtr = std::unique_ptr;
/*
* create --
*
* Creates AtomicHashArray objects. Use instead of constructor/destructor.
*
* We do things this way in order to avoid the perf penalty of a second
* pointer indirection when composing these into AtomicHashMap, which needs
* to store an array of pointers so that it can perform atomic operations on
* them when growing.
*
* Instead of a mess of arguments, we take a max size and a Config struct to
* simulate named ctor parameters. The Config struct has sensible defaults
* for everything, but is overloaded - if you specify a positive capacity,
* that will be used directly instead of computing it based on
* maxLoadFactor.
*
* Create returns an AHA::SmartPtr which is a unique_ptr with a custom
* deleter to make sure everything is cleaned up properly.
*/
struct Config {
KeyT emptyKey;
KeyT lockedKey;
KeyT erasedKey;
double maxLoadFactor;
double growthFactor;
uint32_t entryCountThreadCacheSize;
size_t capacity; // if positive, overrides maxLoadFactor
// Cannot have constexpr ctor because some compilers rightly complain.
Config()
: emptyKey((KeyT)-1),
lockedKey((KeyT)-2),
erasedKey((KeyT)-3),
maxLoadFactor(0.8),
growthFactor(-1),
entryCountThreadCacheSize(1000),
capacity(0) {}
};
// Cannot have pre-instantiated const Config instance because of SIOF.
static SmartPtr create(size_t maxSize, const Config& c = Config());
/*
* find --
*
*
* Returns the iterator to the element if found, otherwise end().
*
* As an optional feature, the type of the key to look up (LookupKeyT) is
* allowed to be different from the type of keys actually stored (KeyT).
*
* This enables use cases where materializing the key is costly and usually
* redundant, e.g., canonicalizing/interning a set of strings and being able
* to look up by StringPiece. To use this feature, LookupHashFcn must take
* a LookupKeyT, and LookupEqualFcn must take KeyT and LookupKeyT as first
* and second parameter, respectively.
*
* See folly/test/ArrayHashArrayTest.cpp for sample usage.
*/
template <
typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
iterator find(LookupKeyT k) {
return iterator(
this, findInternal(k).idx);
}
template <
typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
const_iterator find(LookupKeyT k) const {
return const_cast(this)
->find(k);
}
/*
* insert --
*
* Returns a pair with iterator to the element at r.first and bool success.
* Retrieve the index with ret.first.getIndex().
*
* Fails on key collision (does not overwrite) or if map becomes
* full, at which point no element is inserted, iterator is set to end(),
* and success is set false. On collisions, success is set false, but the
* iterator is set to the existing entry.
*/
std::pair insert(const value_type& r) {
return emplace(r.first, r.second);
}
std::pair insert(value_type&& r) {
return emplace(r.first, std::move(r.second));
}
/*
* emplace --
*
* Same contract as insert(), but performs in-place construction
* of the value type using the specified arguments.
*
* Also, like find(), this method optionally allows 'key_in' to have a type
* different from that stored in the table; see find(). If and only if no
* equal key is already present, this method converts 'key_in' to a key of
* type KeyT using the provided LookupKeyToKeyFcn.
*/
template <
typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal,
typename LookupKeyToKeyFcn = key_convert,
typename... ArgTs>
std::pair emplace(LookupKeyT key_in, ArgTs&&... vCtorArgs) {
SimpleRetT ret = insertInternal<
LookupKeyT,
LookupHashFcn,
LookupEqualFcn,
LookupKeyToKeyFcn>(key_in, std::forward(vCtorArgs)...);
return std::make_pair(iterator(this, ret.idx), ret.success);
}
// returns the number of elements erased - should never exceed 1
size_t erase(KeyT k);
// clears all keys and values in the map and resets all counters. Not thread
// safe.
void clear();
// Exact number of elements in the map - note that readFull() acquires a
// mutex. See folly/ThreadCachedInt.h for more details.
size_t size() const {
return numEntries_.readFull() - numErases_.load(std::memory_order_relaxed);
}
bool empty() const { return size() == 0; }
iterator begin() {
iterator it(this, 0);
it.advancePastEmpty();
return it;
}
const_iterator begin() const {
const_iterator it(this, 0);
it.advancePastEmpty();
return it;
}
iterator end() { return iterator(this, capacity_); }
const_iterator end() const { return const_iterator(this, capacity_); }
// See AtomicHashMap::findAt - access elements directly
// WARNING: The following 2 functions will fail silently for hashtable
// with capacity > 2^32
iterator findAt(uint32_t idx) {
DCHECK_LT(idx, capacity_);
return iterator(this, idx);
}
const_iterator findAt(uint32_t idx) const {
return const_cast(this)->findAt(idx);
}
iterator makeIter(size_t idx) { return iterator(this, idx); }
const_iterator makeIter(size_t idx) const {
return const_iterator(this, idx);
}
// The max load factor allowed for this map
double maxLoadFactor() const { return ((double)maxEntries_) / capacity_; }
void setEntryCountThreadCacheSize(uint32_t newSize) {
numEntries_.setCacheSize(newSize);
numPendingEntries_.setCacheSize(newSize);
}
uint32_t getEntryCountThreadCacheSize() const {
return numEntries_.getCacheSize();
}
/* Private data and helper functions... */
private:
friend class AtomicHashMap<
KeyT,
ValueT,
HashFcn,
EqualFcn,
Allocator,
ProbeFcn>;
struct SimpleRetT {
size_t idx;
bool success;
SimpleRetT(size_t i, bool s) : idx(i), success(s) {}
SimpleRetT() = default;
};
template <
typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal,
typename LookupKeyToKeyFcn = Identity,
typename... ArgTs>
SimpleRetT insertInternal(LookupKeyT key, ArgTs&&... vCtorArgs);
template <
typename LookupKeyT = key_type,
typename LookupHashFcn = hasher,
typename LookupEqualFcn = key_equal>
SimpleRetT findInternal(const LookupKeyT key);
template
void checkLegalKeyIfKey(MaybeKeyT key) {
detail::checkLegalKeyIfKeyTImpl(key, kEmptyKey_, kLockedKey_, kErasedKey_);
}
static std::atomic* cellKeyPtr(const value_type& r) {
// We need some illegal casting here in order to actually store
// our value_type as a std::pair. But a little bit of
// undefined behavior never hurt anyone ...
static_assert(
sizeof(std::atomic) == sizeof(KeyT),
"std::atomic is implemented in an unexpected way for AHM");
return const_cast*>(
reinterpret_cast const*>(&r.first));
}
static KeyT relaxedLoadKey(const value_type& r) {
return cellKeyPtr(r)->load(std::memory_order_relaxed);
}
static KeyT acquireLoadKey(const value_type& r) {
return cellKeyPtr(r)->load(std::memory_order_acquire);
}
// Fun with thread local storage - atomic increment is expensive
// (relatively), so we accumulate in the thread cache and periodically
// flush to the actual variable, and walk through the unflushed counts when
// reading the value, so be careful of calling size() too frequently. This
// increases insertion throughput several times over while keeping the count
// accurate.
ThreadCachedInt numEntries_; // Successful key inserts
ThreadCachedInt numPendingEntries_; // Used by insertInternal
std::atomic