diff --git a/devel/folly/Portfile b/devel/folly/Portfile index ac4d37f837de3..ca1a35638d8eb 100644 --- a/devel/folly/Portfile +++ b/devel/folly/Portfile @@ -15,6 +15,9 @@ if {[string match *clang* ${configure.compiler}]} { legacysupport.use_mp_libcxx yes } +# NB: Facebook does not do API stabiilty, apparently, so please don't +# upgrade without also upgrading its dependents, as listed by: +# port list rdepends:folly github.setup facebook folly 2022.08.08.00 v revision 0 checksums rmd160 aaaf802ce3318fb2a666201779d8d254a53e6dec \ diff --git a/sysutils/watchman/Portfile b/sysutils/watchman/Portfile index f5cc21e1dc262..c54d131154cee 100644 --- a/sysutils/watchman/Portfile +++ b/sysutils/watchman/Portfile @@ -7,8 +7,8 @@ PortGroup boost 1.0 PortGroup rust 1.0 PortGroup compiler_blacklist_versions 1.0 -github.setup facebook watchman 2022.06.06.00 v -revision 3 +github.setup facebook watchman 2022.08.22.00 v +revision 0 categories sysutils platforms darwin @@ -30,14 +30,12 @@ depends_build-append \ depends_lib-append port:pcre \ port:folly \ port:libevent \ - port:fizz \ - port:wangle \ - port:fbthrift \ - port:fb303 \ port:google-glog \ port:${port_libfmt} configure.args-append \ + -DENABLE_EDEN_SUPPORT=NO \ + -DPython3_Development_FOUND=NO \ -DBoost_INCLUDE_DIR=[boost::include_dir] configure.env-append \ @@ -45,7 +43,8 @@ configure.env-append \ YARN_PATH=${prefix}/bin/yarn \ DESTDIR=${destroot} -patchfiles cmake-rust-build-target.diff +patchfiles cmake-rust-build-target.diff \ + revert-optionset-move.diff post-patch { reinplace "s,/usr/bin,${prefix}," CMakeLists.txt @@ -70,9 +69,9 @@ compiler.cxx_standard \ 2017 checksums ${distname}${extract.suffix} \ - rmd160 f172dae7f2d6f182bac266359ba14089af66ccd8 \ - sha256 d24548e0c4d5c692c15b5a8eaf46b02cb59d9fb228d611c31b6c8e81e7a4cb2e \ - size 3775957 + rmd160 ccdc1f38d6a6d68c9bea0ef76267893ddec901ee \ + sha256 2a500e68317953b882276a979a3205c08a348c8910b9e33a4bcf606d0ab580f7 \ + size 3785713 cargo.crates \ diff --git a/sysutils/watchman/files/revert-optionset-move.diff b/sysutils/watchman/files/revert-optionset-move.diff new file mode 100644 index 0000000000000..7fc73ed8c3642 --- /dev/null +++ b/sysutils/watchman/files/revert-optionset-move.diff @@ -0,0 +1,558 @@ +diff --git CMakeLists.txt CMakeLists.txt +--- CMakeLists.txt ++++ CMakeLists.txt +@@ -342,10 +342,10 @@ find_package( + find_package(LibEvent REQUIRED) + get_filename_component(LIBEVENT_LIBDIR "${LIBEVENT_LIB}" DIRECTORY) + link_directories(${LIBEVENT_LIBDIR}) +-find_package(edencommon CONFIG REQUIRED) + find_package(fmt CONFIG REQUIRED) + find_package(folly CONFIG REQUIRED) + ++ + if (ENABLE_EDEN_SUPPORT) + find_package(fizz CONFIG REQUIRED) + find_package(wangle CONFIG REQUIRED) +@@ -450,7 +450,6 @@ target_link_libraries(third_party_deps I + gflags + ${Boost_LIBRARIES} + fmt::fmt +- edencommon::edencommon_utils + ) + target_include_directories(third_party_deps INTERFACE + ${FOLLY_INCLUDE_DIR} +@@ -773,6 +772,7 @@ t_test(ignore watchman/test/BserTest.cpp + #t_test(inmemoryview watchman/test/InMemoryViewTest.cpp) + t_test(log watchman/test/LogTest.cpp) + t_test(maputil watchman/test/MapUtilTest.cpp) ++t_test(optionset watchman/test/OptionSetTest.cpp) + t_test(pendingcollection watchman/test/PendingCollectionTest.cpp) + # Linking this test needs the targets graph to be cleaned up. + #t_test(perfsample watchman/test/PerfSampleTest.cpp) +diff --git build/fbcode_builder/manifests/watchman build/fbcode_builder/manifests/watchman +--- build/fbcode_builder/manifests/watchman ++++ build/fbcode_builder/manifests/watchman +@@ -13,7 +13,6 @@ builder = cmake + [dependencies] + boost + cpptoml +-edencommon + fb303 + fbthrift + folly +diff --git watchman/Client.cpp watchman/Client.cpp +--- watchman/Client.cpp ++++ watchman/Client.cpp +@@ -23,17 +23,6 @@ namespace watchman { + + namespace { + +-using namespace facebook::eden; +- +-ProcessNameCache& getProcessNameCache() { +- static auto* pnc = new ProcessNameCache; +- return *pnc; +-} +- +-ProcessNameHandle lookupProcessName(pid_t pid) { +- return getProcessNameCache().lookup(pid); +-} +- + constexpr size_t kResponseLogLimit = 0; + + folly::Synchronized> clients; +@@ -220,10 +209,7 @@ void UserClient::create(std::unique_ptr< + } + + UserClient::UserClient(PrivateBadge, std::unique_ptr stm) +- : Client{std::move(stm)}, +- since_{std::chrono::system_clock::now()}, +- peerPid_{this->stm->getPeerProcessID()}, +- peerName_{lookupProcessName(peerPid_)} { ++ : Client(std::move(stm)) { + clients.wlock()->insert(this); + } + +@@ -260,13 +246,6 @@ std::vector UserClien + ClientDebugStatus UserClient::getDebugStatus() const { + ClientDebugStatus rv; + rv.state = status_.getName(); +- if (peerPid_) { +- rv.peer.emplace(); +- rv.peer->pid = peerPid_; +- // May briefly, once, block on the ProcessNameCache thread. +- rv.peer->name = peerName_.get(); +- } +- rv.since = std::chrono::system_clock::to_time_t(since_); + return rv; + } + +diff --git watchman/Client.h watchman/Client.h +--- watchman/Client.h ++++ watchman/Client.h +@@ -6,14 +6,9 @@ + */ + + #pragma once +- +-#include + #include +- +-#include + #include + #include +- + #include "watchman/Clock.h" + #include "watchman/CommandRegistry.h" + #include "watchman/Logging.h" +@@ -159,27 +154,12 @@ class ClientStatus { + std::atomic state_{THREAD_STARTING}; + }; + +-struct PeerInfo : serde::Object { +- int32_t pid; +- std::string name; +- +- template +- void map(X& x) { +- x("pid", pid); +- x("name", name); +- } +-}; +- + struct ClientDebugStatus : serde::Object { + std::string state; +- std::optional peer; +- std::optional since; + + template + void map(X& x) { + x("state", state); +- x("peer", peer); +- x("since", since); + } + }; + +@@ -234,10 +214,6 @@ class UserClient final : public Client { + + void clientThread() noexcept; + +- const std::chrono::system_clock::time_point since_; +- const pid_t peerPid_; +- const facebook::eden::ProcessNameHandle peerName_; +- + ClientStatus status_; + }; + +diff --git watchman/CommandRegistry.h watchman/CommandRegistry.h +--- watchman/CommandRegistry.h ++++ watchman/CommandRegistry.h +@@ -12,7 +12,7 @@ + + // TODO: We could avoid the Client dependency fi CommandHandler returned a + // json_ref response or error rather than taking a Client. +-#include "eden/common/utils/OptionSet.h" ++#include "watchman/OptionSet.h" + #include "watchman/Serde.h" + #include "watchman/thirdparty/jansson/jansson.h" + #include "watchman/watchman_preprocessor.h" +@@ -101,7 +101,7 @@ using CommandHandler = + */ + using ResultPrinter = void (*)(const json_ref& result); + +-struct CommandFlags : facebook::eden::OptionSet {}; ++struct CommandFlags : OptionSet {}; + + inline constexpr auto CMD_DAEMON = CommandFlags::raw(1); + inline constexpr auto CMD_CLIENT = CommandFlags::raw(2); +diff --git watchman/OptionSet.h watchman/OptionSet.h +new file mode 100644 +--- /dev/null ++++ watchman/OptionSet.h +@@ -0,0 +1,207 @@ ++/* ++ * 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. ++ */ ++ ++#pragma once ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++namespace watchman { ++ ++/** ++ * Typed wrapper around bit sets. Inspired by Swift's OptionSet. ++ * ++ * See OptionSetTest.cpp for example usage. ++ */ ++template ++class OptionSet { ++ static_assert(std::is_integral_v); ++ using zero_t = struct zero_t***; ++ ++ public: ++ using UnderlyingType = Underlying; ++ using NameTable = std::initializer_list>; ++ ++ constexpr OptionSet() = default; ++ ++ /** ++ * Allows initialization from literal 0. ++ */ ++ /* implicit */ constexpr OptionSet(zero_t) {} ++ ++ ///* implicit */ constexpr OptionSet(Derived v) : value_{v.value_} {} ++ ++ /* implicit */ constexpr OptionSet(std::initializer_list args) { ++ for (Derived v : args) { ++ value_ |= v; ++ } ++ } ++ ++ constexpr static Derived raw(Underlying raw) { ++ Derived d; ++ d.value_ = raw; ++ return d; ++ } ++ ++ explicit operator bool() const { ++ return value_ != 0; ++ } ++ ++ /** ++ * Returns the raw bits. ++ */ ++ Underlying asRaw() const { ++ return value_; ++ } ++ ++ /** ++ * Returns true if every bit in `set` is set here too. ++ * ++ * Alias for `containsAllOf`. ++ */ ++ bool contains(Derived set) const { ++ return containsAllOf(set); ++ } ++ ++ /** ++ * Returns true if every bit in `set` is set here too. ++ */ ++ bool containsAllOf(Derived set) const { ++ return (value_ & set.value_) == set.value_; ++ } ++ ++ /** ++ * Returns true if any bit in `set` in set here too. ++ */ ++ bool containsAnyOf(Derived set) const { ++ return (value_ & set.value_) != 0; ++ } ++ ++ /** ++ * Returns true if all bits in `set` are unset here. ++ */ ++ bool containsNoneOf(Derived set) const { ++ return (value_ & set.value_) == 0; ++ } ++ ++ /** ++ * Returns true if no bits are set. ++ */ ++ bool empty() const { ++ return value_ == 0; ++ } ++ ++ /** ++ * Turns on the specified bits. ++ */ ++ void set(Derived set) { ++ value_ |= set.value_; ++ } ++ ++ /** ++ * Clears the specified bits. ++ */ ++ void clear(Derived set) { ++ value_ &= ~set.value_; ++ } ++ ++ /** ++ * Returns a space-delimited string representing the names of each set bit. ++ * The name mapping is defined by `Derived::table`. ++ */ ++ std::string format() const { ++ // TODO: It might be nice to move the implementation of this function behind ++ // a template on Raw to reduce the number of possible expansions. That would ++ // require some sort of constexpr mapping from Derived onto Raw. Perhaps ++ // that's as easy as asserting they have the same representation and casting ++ // pointers. ++ ++ constexpr const NameTable& table = Derived::table; ++ ++ if (empty() || std::empty(table)) { ++ return {}; ++ } ++ ++ // Precompute the length to avoid multiple allocations. ++ size_t length = 0; ++ for (auto [bit, name] : table) { ++ // TODO: Should this assert bits are exact powers of two? ++ if (containsAllOf(bit)) { ++ if (length) { ++ length++; ++ } ++ length += name.size(); ++ } ++ } ++ ++ std::string result(length, 0); ++ char* p = result.data(); ++ ++ for (auto [bit, name] : table) { ++ if (containsAllOf(bit)) { ++ if (p != result.data()) { ++ *p++ = ' '; ++ } ++ memcpy(p, name.data(), name.size()); ++ p += name.size(); ++ } ++ } ++ ++ assert(static_cast(p - result.data()) == length); ++ ++ return result; ++ } ++ ++ template ++ friend std::basic_ostream& operator<<( ++ std::basic_ostream& out, ++ const OptionSet& opts) { ++ return out << opts.format(); ++ } ++ ++ Derived& operator|=(Derived that) { ++ value_ |= that.value_; ++ return static_cast(*this); ++ } ++ ++ Derived& operator&=(Derived that) { ++ value_ &= that.value_; ++ return static_cast(*this); ++ } ++ ++ /** ++ * Returns the union of both sets. ++ */ ++ constexpr friend Derived operator|(Derived lhs, Derived rhs) { ++ return raw(lhs.value_ | rhs.value_); ++ } ++ ++ /** ++ * Returns the intersection of both sets. ++ */ ++ constexpr friend Derived operator&(Derived lhs, Derived rhs) { ++ return raw(lhs.value_ & rhs.value_); ++ } ++ ++ constexpr friend bool operator==(Derived lhs, Derived rhs) { ++ return lhs.value_ == rhs.value_; ++ } ++ ++ constexpr friend bool operator!=(Derived lhs, Derived rhs) { ++ return lhs.value_ != rhs.value_; ++ } ++ ++ private: ++ Underlying value_{}; ++}; ++ ++} // namespace watchman +diff --git watchman/PendingCollection.h watchman/PendingCollection.h +--- watchman/PendingCollection.h ++++ watchman/PendingCollection.h +@@ -11,7 +11,7 @@ + #include + #include + #include +-#include "eden/common/utils/OptionSet.h" ++#include "watchman/OptionSet.h" + #include "watchman/thirdparty/libart/src/art.h" + #include "watchman/watchman_string.h" + +@@ -19,7 +19,7 @@ struct watchman_dir; + + namespace watchman { + +-struct PendingFlags : facebook::eden::OptionSet { ++struct PendingFlags : OptionSet { + using OptionSet::OptionSet; + static const NameTable table; + }; +diff --git watchman/cmds/debug.cpp watchman/cmds/debug.cpp +--- watchman/cmds/debug.cpp ++++ watchman/cmds/debug.cpp +@@ -5,10 +5,7 @@ + * LICENSE file in the root directory of this source tree. + */ + +-#include +-#include + #include +-#include + #include + #include "watchman/Client.h" + #include "watchman/InMemoryView.h" +@@ -260,28 +257,6 @@ W_CMD_REG( + CMD_DAEMON, + w_cmd_realpath_root); + +-namespace { +- +-std::string shellQuoteCommand(std::string_view command) { +- std::vector argv; +- folly::split('\0', command, argv); +- +- // Every argument in a command line is null-terminated. Remove the last, empty +- // argument. +- if (argv.size() && argv.back().empty()) { +- argv.pop_back(); +- } +- +- for (auto& arg : argv) { +- // TODO: shellQuote is not particularly good. It always brackets with ' and +- // does not handle non-printable characters. We should write our own. +- arg = folly::shellQuote(arg); +- } +- return folly::join(' ', argv); +-} +- +-} // namespace +- + struct DebugStatusCommand : PrettyCommand { + static constexpr std::string_view name = "debug-status"; + +@@ -328,19 +303,7 @@ struct DebugStatusCommand : PrettyComman + + fmt::print("CLIENTS\n-------\n"); + for (auto& client : response.clients) { +- if (client.peer) { +- fmt::print( +- "{}: {}\n", client.peer->pid, shellQuoteCommand(client.peer->name)); +- } else { +- fmt::print("unknown peer\n"); +- } +- if (client.since) { +- fmt::print( +- " - since: {}\n", +- std::chrono::system_clock::from_time_t(client.since.value())); +- } + fmt::print(" - state: {}\n", client.state); +- fmt::print("\n"); + } + } + }; +diff --git watchman/test/OptionSetTest.cpp watchman/test/OptionSetTest.cpp +new file mode 100644 +--- /dev/null ++++ watchman/test/OptionSetTest.cpp +@@ -0,0 +1,90 @@ ++/* ++ * 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. ++ */ ++ ++#include "watchman/OptionSet.h" ++#include ++ ++using namespace watchman; ++ ++namespace { ++ ++struct ColorSet : OptionSet { ++ using OptionSet::OptionSet; ++ static const NameTable table; ++}; ++ ++constexpr inline auto CM_RED = ColorSet::raw(1); ++constexpr inline auto CM_GREEN = ColorSet::raw(2); ++constexpr inline auto CM_BLUE = ColorSet::raw(4); ++ ++const ColorSet::NameTable ColorSet::table = { ++ {CM_RED, "RED"}, ++ {CM_GREEN, "GREEN"}, ++ {CM_BLUE, "BLUE"}, ++}; ++ ++} // namespace ++ ++TEST(OptionSet, initialization_from_zero) { ++ ColorSet set = 0; ++ set = 0; ++ ++ // Does not compile: ++ // set = 1; ++ ++ // Does not compile: ++ // int i = 0; ++ // set = i; ++} ++ ++TEST(OptionSet, default_is_empty) { ++ ColorSet set; ++ EXPECT_FALSE(set); ++} ++ ++TEST(OptionSet, assignment_operators) { ++ ColorSet set; ++ set |= CM_RED; ++ EXPECT_EQ(CM_RED, set); ++ set &= CM_GREEN; ++ EXPECT_EQ(ColorSet{}, set); ++} ++ ++TEST(OptionSet, format) { ++ EXPECT_EQ("", ColorSet{}.format()); ++ EXPECT_EQ("RED", CM_RED.format()); ++ EXPECT_EQ("GREEN", CM_GREEN.format()); ++ EXPECT_EQ("BLUE", CM_BLUE.format()); ++ EXPECT_EQ("RED GREEN", (CM_RED | CM_GREEN).format()); ++ EXPECT_EQ("GREEN BLUE", (CM_GREEN | CM_BLUE).format()); ++ EXPECT_EQ("RED GREEN BLUE", (CM_RED | CM_GREEN | CM_BLUE).format()); ++} ++ ++TEST(OptionSet, containsAllOf) { ++ EXPECT_TRUE((CM_RED | CM_GREEN).contains(CM_RED)); ++ EXPECT_FALSE((CM_RED | CM_GREEN).contains(CM_BLUE)); ++ EXPECT_FALSE((CM_RED | CM_GREEN).contains(CM_RED | CM_BLUE)); ++ ++ EXPECT_TRUE((CM_RED | CM_GREEN).containsAllOf(CM_RED)); ++ EXPECT_FALSE((CM_RED | CM_GREEN).containsAllOf(CM_BLUE)); ++ EXPECT_FALSE((CM_RED | CM_GREEN).containsAllOf(CM_RED | CM_BLUE)); ++} ++ ++TEST(OptionSet, intersect) { ++ EXPECT_FALSE(CM_RED & CM_BLUE); ++ EXPECT_EQ(CM_GREEN, (CM_RED | CM_GREEN) & (CM_GREEN | CM_BLUE)); ++} ++ ++TEST(OptionSet, containsAnyOf) { ++ EXPECT_TRUE((CM_RED | CM_GREEN).containsAnyOf(CM_GREEN | CM_BLUE)); ++ EXPECT_FALSE((CM_RED | CM_GREEN).containsAnyOf(CM_BLUE)); ++} ++ ++TEST(OptionSet, containsNoneOf) { ++ EXPECT_FALSE((CM_RED | CM_GREEN).containsNoneOf(CM_GREEN | CM_BLUE)); ++ EXPECT_TRUE(CM_RED.containsNoneOf(CM_GREEN | CM_BLUE)); ++}