Skip to content

Commit 59ca636

Browse files
authored
Merge pull request #131 from wravery/master
Re-add the Boost.Filesystem fallback
2 parents 3add6d3 + 2315adb commit 59ca636

File tree

10 files changed

+141
-31
lines changed

10 files changed

+141
-31
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ endfunction()
4545

4646
find_package(Threads MODULE REQUIRED)
4747

48-
find_package(pegtl 3.0.0 QUIET CONFIG)
48+
find_package(pegtl 3.0.1 QUIET CONFIG)
4949
if(NOT pegtl_FOUND)
5050
# If a compatible version of PEGTL is not already installed, build and install it from the submodule directory.
5151
set(PEGTL_BUILD_TESTS OFF CACHE BOOL "Disable PEGTL tests")

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ The easiest way to get all of these and to build `cppgraphqlgen` in one step is
4545
[microsoft/vcpkg](https://github.com/microsoft/vcpkg). To install with vcpkg, make sure you've pulled the latest version
4646
and then run `vcpkg install cppgraphqlgen` (or `cppgraphqlgen:x64-windows`, `cppgraphqlgen:x86-windows-static`, etc.
4747
depending on your platform). To install just the dependencies and work in a clone of this repo, you'll need some subset
48-
of `vcpkg install pegtl boost-program-options rapidjson gtest`. It works for Windows, Linux, and Mac,
48+
of `vcpkg install pegtl boost-program-options boost-filesystem rapidjson gtest`. It works for Windows, Linux, and Mac,
4949
but if you want to try building for another platform (e.g. Android or iOS), you'll need to do more of this manually.
5050

5151
Manual installation will work best if you clone the GitHub repos for each of the dependencies and follow the installation
@@ -85,6 +85,11 @@ do that.
8585

8686
I'm using [Boost](https://www.boost.org/doc/libs/1_69_0/more/getting_started/index.html) for `schemagen`:
8787

88+
- C++17 std::filesystem support on Unix:
89+
[Boost.Filesystem](https://www.boost.org/doc/libs/1_69_0/libs/filesystem/doc/index.htm). Most of the default C++
90+
compilers on Linux still have `std::filesystem` from C++17 in an experimental directory and require an extra
91+
library. The standard just adopted the Boost library, so on Unix systems I have an `#ifdef` which redirects back to
92+
it for the time being.
8893
- Command line handling: [Boost.Program_options](https://www.boost.org/doc/libs/1_69_0/doc/html/program_options.html).
8994
Run `schemagen -?` to get a list of options. Many of the files in the [samples](samples/) directory were generated
9095
with `schemagen`, you can look at [samples/CMakeLists.txt](samples/CMakeLists.txt) for a few examples of how to call it:

cmake/test_filesystem.cpp.in

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This is a dummy program that just needs to compile and link to tell us if
2+
// the C++17 std::filesystem API is available. Use CMake's configure_file
3+
// command to replace the FILESYSTEM_HEADER and FILESYSTEM_NAMESPACE tokens
4+
// for each combination of headers and namespaces which we want to pass to the
5+
// CMake try_compile command.
6+
7+
#include <@FILESYSTEM_HEADER@>
8+
9+
int main()
10+
{
11+
try
12+
{
13+
throw @FILESYSTEM_NAMESPACE@::filesystem_error("instantiate one to make sure it links",
14+
std::make_error_code(std::errc::function_not_supported));
15+
}
16+
catch (const @FILESYSTEM_NAMESPACE@::filesystem_error& error)
17+
{
18+
return -1;
19+
}
20+
21+
return !@FILESYSTEM_NAMESPACE@::temp_directory_path().is_absolute();
22+
}

include/Validation.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,22 +227,26 @@ class ValidateExecutableVisitor
227227
using OperationVariables = std::optional<VariableTypes>;
228228
using VariableSet = std::set<std::string>;
229229

230+
// These members store Introspection schema information which does not change between queries.
230231
OperationTypes _operationTypes;
231232
ValidateTypeKinds _typeKinds;
232233
MatchingTypes _matchingTypes;
233234
Directives _directives;
234235
EnumValues _enumValues;
235236
ScalarTypes _scalarTypes;
236237

238+
// These members store information that's specific to a single query and changes every time we
239+
// visit a new one. They must be reset in between queries.
237240
ExecutableNodes _fragmentDefinitions;
238241
ExecutableNodes _operationDefinitions;
242+
FragmentSet _referencedFragments;
243+
FragmentSet _fragmentCycles;
239244

245+
// These members store state for the visitor. They implicitly reset each time we call visit.
240246
OperationVariables _operationVariables;
241247
VariableDefinitions _variableDefinitions;
242248
VariableSet _referencedVariables;
243-
FragmentSet _referencedFragments;
244249
FragmentSet _fragmentStack;
245-
FragmentSet _fragmentCycles;
246250
size_t _fieldCount = 0;
247251
TypeFields _typeFields;
248252
InputTypeFields _inputTypeFields;

include/graphqlservice/GraphQLService.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -947,14 +947,17 @@ struct SubscriptionData : std::enable_shared_from_this<SubscriptionData>
947947
const peg::ast_node& selection;
948948
};
949949

950+
// Forward declare just the class type so we can reference it in the Request::_validation member.
951+
class ValidateExecutableVisitor;
952+
950953
// Request scans the fragment definitions and finds the right operation definition to interpret
951954
// depending on the operation name (which might be empty for a single-operation document). It
952955
// also needs the values of the request variables.
953956
class Request : public std::enable_shared_from_this<Request>
954957
{
955958
protected:
956959
GRAPHQLSERVICE_EXPORT explicit Request(TypeMap&& operationTypes);
957-
GRAPHQLSERVICE_EXPORT virtual ~Request() = default;
960+
GRAPHQLSERVICE_EXPORT virtual ~Request();
958961

959962
public:
960963
GRAPHQLSERVICE_EXPORT std::vector<schema_error> validate(peg::ast& query) const;
@@ -1025,6 +1028,7 @@ class Request : public std::enable_shared_from_this<Request>
10251028
const std::string& operationName, response::Value&& variables) const;
10261029

10271030
TypeMap _operations;
1031+
std::unique_ptr<ValidateExecutableVisitor> _validation;
10281032
std::map<SubscriptionKey, std::shared_ptr<SubscriptionData>> _subscriptions;
10291033
std::unordered_map<SubscriptionName, std::set<SubscriptionKey>> _listeners;
10301034
SubscriptionKey _nextKey = 0;

src/CMakeLists.txt

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,55 @@ if(GRAPHQL_BUILD_SCHEMAGEN)
5050

5151
set(BOOST_COMPONENTS program_options)
5252
set(BOOST_LIBRARIES Boost::program_options)
53-
53+
54+
# Try compiling a test program with std::filesystem or one of its alternatives.
55+
function(check_filesystem_impl FILESYSTEM_HEADER FILESYSTEM_NAMESPACE OPTIONAL_LIBS OUT_RESULT)
56+
set(TEST_FILE "test_${OUT_RESULT}.cpp")
57+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../cmake/test_filesystem.cpp.in ${TEST_FILE} @ONLY)
58+
59+
try_compile(TEST_RESULT
60+
${CMAKE_CURRENT_BINARY_DIR}
61+
${CMAKE_CURRENT_BINARY_DIR}/${TEST_FILE}
62+
CXX_STANDARD 17)
63+
64+
if(NOT TEST_RESULT)
65+
# Retry with each of the optional libraries.
66+
foreach(OPTIONAL_LIB IN LISTS OPTIONAL_LIBS)
67+
try_compile(TEST_RESULT
68+
${CMAKE_CURRENT_BINARY_DIR}
69+
${CMAKE_CURRENT_BINARY_DIR}/${TEST_FILE}
70+
LINK_LIBRARIES ${OPTIONAL_LIB}
71+
CXX_STANDARD 17)
72+
73+
if(TEST_RESULT)
74+
# Looks like the optional library was required, go ahead and add it to the link options.
75+
target_link_libraries(schemagen PRIVATE ${OPTIONAL_LIB})
76+
break()
77+
endif()
78+
endforeach(OPTIONAL_LIB)
79+
endif()
80+
81+
set(${OUT_RESULT} ${TEST_RESULT} PARENT_SCOPE)
82+
endfunction(check_filesystem_impl)
83+
84+
# Try compiling a minimal program with each header/namespace, in order of preference:
85+
# C++17: #include <filesystem> // std::filesystem
86+
# Experimental C++17: #include <experimental/filesystem> // std::experimental::filesystem
87+
# Boost.Filesystem: #include <boost/filesystem.hpp> // boost::filesystem
88+
check_filesystem_impl("filesystem" "std::filesystem" "stdc++fs;c++fs" STD_FILESYTEM)
89+
if(STD_FILESYTEM)
90+
target_compile_definitions(schemagen PRIVATE USE_STD_FILESYSTEM)
91+
else()
92+
check_filesystem_impl("experimental/filesystem" "std::experimental::filesystem" "stdc++fs;c++fs" STD_EXPERIMENTAL_FILESYTEM)
93+
if(STD_EXPERIMENTAL_FILESYTEM)
94+
target_compile_definitions(schemagen PRIVATE USE_STD_EXPERIMENTAL_FILESYSTEM)
95+
else()
96+
set(BOOST_COMPONENTS ${BOOST_COMPONENTS} filesystem)
97+
set(BOOST_LIBRARIES ${BOOST_LIBRARIES} Boost::filesystem)
98+
target_compile_definitions(schemagen PRIVATE USE_BOOST_FILESYSTEM)
99+
endif()
100+
endif()
101+
54102
find_package(Boost REQUIRED COMPONENTS ${BOOST_COMPONENTS})
55103
target_link_libraries(schemagen PRIVATE ${BOOST_LIBRARIES})
56104

src/GraphQLService.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,20 +1759,25 @@ void SubscriptionDefinitionVisitor::visitInlineFragment(const peg::ast_node& inl
17591759

17601760
Request::Request(TypeMap&& operationTypes)
17611761
: _operations(std::move(operationTypes))
1762+
, _validation(std::make_unique<ValidateExecutableVisitor>(*this))
17621763
{
17631764
}
17641765

1766+
Request::~Request()
1767+
{
1768+
// The default implementation is fine, but it can't be declared as = default because it needs to
1769+
// know how to destroy the _validation member and it can't do that with just a forward
1770+
// declaration of the class.
1771+
}
1772+
17651773
std::vector<schema_error> Request::validate(peg::ast& query) const
17661774
{
17671775
std::vector<schema_error> errors;
17681776

17691777
if (!query.validated)
17701778
{
1771-
ValidateExecutableVisitor visitor(*this);
1772-
1773-
visitor.visit(*query.root);
1774-
1775-
errors = visitor.getStructuredErrors();
1779+
_validation->visit(*query.root);
1780+
errors = _validation->getStructuredErrors();
17761781
query.validated = errors.empty();
17771782
}
17781783

src/SchemaGenerator.cpp

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,26 @@
55

66
#include <boost/program_options.hpp>
77

8+
// clang-format off
9+
#ifdef USE_STD_FILESYSTEM
10+
#include <filesystem>
11+
namespace fs = std::filesystem;
12+
#else
13+
#ifdef USE_STD_EXPERIMENTAL_FILESYSTEM
14+
#include <experimental/filesystem>
15+
namespace fs = std::experimental::filesystem;
16+
#else
17+
#ifdef USE_BOOST_FILESYSTEM
18+
#include <boost/filesystem.hpp>
19+
namespace fs = boost::filesystem;
20+
#else
21+
#error "No std::filesystem implementation defined"
22+
#endif
23+
#endif
24+
#endif
25+
// clang-format on
26+
827
#include <cctype>
9-
#include <filesystem>
1028
#include <fstream>
1129
#include <iostream>
1230
#include <regex>
@@ -297,11 +315,11 @@ std::string Generator::getHeaderDir() const noexcept
297315
{
298316
if (_isIntrospection)
299317
{
300-
return (std::filesystem::path { "include" } / "graphqlservice").string();
318+
return (fs::path { "include" } / "graphqlservice").string();
301319
}
302320
else if (_options.paths)
303321
{
304-
return std::filesystem::path { _options.paths->headerPath }.string();
322+
return fs::path { _options.paths->headerPath }.string();
305323
}
306324
else
307325
{
@@ -317,13 +335,13 @@ std::string Generator::getSourceDir() const noexcept
317335
}
318336
else
319337
{
320-
return std::filesystem::path(_options.paths->sourcePath).string();
338+
return fs::path(_options.paths->sourcePath).string();
321339
}
322340
}
323341

324342
std::string Generator::getHeaderPath() const noexcept
325343
{
326-
std::filesystem::path fullPath { _headerDir };
344+
fs::path fullPath { _headerDir };
327345

328346
if (_isIntrospection)
329347
{
@@ -341,7 +359,7 @@ std::string Generator::getObjectHeaderPath() const noexcept
341359
{
342360
if (_options.separateFiles)
343361
{
344-
std::filesystem::path fullPath { _headerDir };
362+
fs::path fullPath { _headerDir };
345363

346364
fullPath /= (_options.customSchema->filenamePrefix + "Objects.h");
347365
return fullPath.string();
@@ -352,7 +370,7 @@ std::string Generator::getObjectHeaderPath() const noexcept
352370

353371
std::string Generator::getSourcePath() const noexcept
354372
{
355-
std::filesystem::path fullPath { _sourceDir };
373+
fs::path fullPath { _sourceDir };
356374

357375
if (_isIntrospection)
358376
{
@@ -1659,8 +1677,7 @@ std::string Generator::getOutputCppType(const OutputField& field) const noexcept
16591677
bool Generator::outputHeader() const noexcept
16601678
{
16611679
std::ofstream headerFile(_headerPath, std::ios_base::trunc);
1662-
IncludeGuardScope includeGuard { headerFile,
1663-
std::filesystem::path(_headerPath).filename().string() };
1680+
IncludeGuardScope includeGuard { headerFile, fs::path(_headerPath).filename().string() };
16641681

16651682
headerFile << R"cpp(#include "graphqlservice/GraphQLService.h"
16661683
@@ -2042,8 +2059,8 @@ bool Generator::outputSource() const noexcept
20422059
)cpp";
20432060
if (!_isIntrospection)
20442061
{
2045-
sourceFile << R"cpp(#include ")cpp"
2046-
<< std::filesystem::path(_objectHeaderPath).filename().string() << R"cpp("
2062+
sourceFile << R"cpp(#include ")cpp" << fs::path(_objectHeaderPath).filename().string()
2063+
<< R"cpp("
20472064
20482065
)cpp";
20492066
}
@@ -3421,8 +3438,8 @@ std::string Generator::getIntrospectionType(
34213438
std::vector<std::string> Generator::outputSeparateFiles() const noexcept
34223439
{
34233440
std::vector<std::string> files;
3424-
const std::filesystem::path headerDir(_headerDir);
3425-
const std::filesystem::path sourceDir(_sourceDir);
3441+
const fs::path headerDir(_headerDir);
3442+
const fs::path sourceDir(_sourceDir);
34263443
std::string queryType;
34273444

34283445
for (const auto& operation : _operationTypes)
@@ -3437,10 +3454,10 @@ std::vector<std::string> Generator::outputSeparateFiles() const noexcept
34373454
// Output a convenience header
34383455
std::ofstream objectHeaderFile(_objectHeaderPath, std::ios_base::trunc);
34393456
IncludeGuardScope includeGuard { objectHeaderFile,
3440-
std::filesystem::path(_objectHeaderPath).filename().string() };
3457+
fs::path(_objectHeaderPath).filename().string() };
34413458

3442-
objectHeaderFile << R"cpp(#include ")cpp"
3443-
<< std::filesystem::path(_headerPath).filename().string() << R"cpp("
3459+
objectHeaderFile << R"cpp(#include ")cpp" << fs::path(_headerPath).filename().string()
3460+
<< R"cpp("
34443461
34453462
)cpp";
34463463

@@ -3474,8 +3491,7 @@ std::vector<std::string> Generator::outputSeparateFiles() const noexcept
34743491
std::ofstream headerFile(headerPath, std::ios_base::trunc);
34753492
IncludeGuardScope includeGuard { headerFile, headerFilename };
34763493

3477-
headerFile << R"cpp(#include ")cpp"
3478-
<< std::filesystem::path(_headerPath).filename().string() << R"cpp("
3494+
headerFile << R"cpp(#include ")cpp" << fs::path(_headerPath).filename().string() << R"cpp("
34793495
34803496
)cpp";
34813497

@@ -3498,7 +3514,7 @@ std::vector<std::string> Generator::outputSeparateFiles() const noexcept
34983514
sourceFile << R"cpp(// Copyright (c) Microsoft Corporation. All rights reserved.
34993515
// Licensed under the MIT License.
35003516
3501-
#include ")cpp" << std::filesystem::path(_objectHeaderPath).filename().string()
3517+
#include ")cpp" << fs::path(_objectHeaderPath).filename().string()
35023518
<< R"cpp("
35033519
35043520
#include "graphqlservice/Introspection.h"

src/Validation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,12 @@ std::vector<schema_error> ValidateExecutableVisitor::getStructuredErrors()
792792
{
793793
auto errors = std::move(_errors);
794794

795+
// Reset all of the state for this query, but keep the Introspection schema information.
796+
_fragmentDefinitions.clear();
797+
_operationDefinitions.clear();
798+
_referencedFragments.clear();
799+
_fragmentCycles.clear();
800+
795801
return errors;
796802
}
797803

0 commit comments

Comments
 (0)