diff --git a/CMakeLists.txt b/CMakeLists.txt index 6114378f..0a7d317c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,7 @@ # See README.md for instruction on how to build SHAD with CMake cmake_minimum_required(VERSION 3.1) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if (NOT DEFINED SHAD_VERSION_MAJOR) set(SHAD_VERSION_MAJOR 0) @@ -114,6 +115,8 @@ configure_file( ${SHAD_INCLUDE_DIR}/shad/config/test-config.h) if (SHAD_RUNTIME_SYSTEM STREQUAL "GMT") + set(RUNTIME_PKGS ${RUNTIME_PKGS} "gmt") + # TODO should be use everytime given the MPI dependencies for collective operations find_package(MPI REQUIRED) include_directories(${MPI_INCLUDE_PATH}) endif() @@ -122,8 +125,12 @@ include_directories(${SHAD_MAIN_INCLUDE_DIR} ${SHAD_INCLUDE_DIR}) configure_file( ${SHAD_MAIN_SRC_DIR}/pkgconfig/shad.pc.in - ${SHAD_BINARY_DIR}/pkgconfig/shad.pc @ONLY) -install(FILES ${SHAD_BINARY_DIR}/pkgconfig/shad.pc DESTINATION pkgconfig) + ${SHAD_BINARY_DIR}/pkgconfig/shad.pc + @ONLY +) +install(FILES ${SHAD_BINARY_DIR}/pkgconfig/shad.pc + DESTINATION "pkgconfig") + install(FILES ${SHAD_MAIN_SRC_DIR}/cmake/FindSHAD.cmake DESTINATION cmake) # Test commands diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5d43c4c9..c14974b7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(array) add_subdirectory(edge_index_graph) +add_subdirectory(clique_covering) add_subdirectory(blackscholes) add_subdirectory(pi) diff --git a/examples/clique_covering/CMakeLists.txt b/examples/clique_covering/CMakeLists.txt new file mode 100644 index 00000000..a197a7ae --- /dev/null +++ b/examples/clique_covering/CMakeLists.txt @@ -0,0 +1,13 @@ +set(program +clique_covering) + + +foreach(p ${program}) + add_executable(${p} ${p}.cc) + # Add debug symbols only for Debug and RelWithDebInfo configs: + # target_compile_options(${p} PRIVATE + # $<$:-g> + # $<$:-g> + # ) + target_link_libraries(${p} ${SHAD_RUNTIME_LIB} runtime) +endforeach(p) diff --git a/examples/clique_covering/clique_covering.cc b/examples/clique_covering/clique_covering.cc new file mode 100644 index 00000000..a448bb46 --- /dev/null +++ b/examples/clique_covering/clique_covering.cc @@ -0,0 +1,124 @@ +//===------------------------------------------------------------*- C++ -*-===// +// +// SHAD +// +// The Scalable High-performance Algorithms and Data Structure Library +// +//===----------------------------------------------------------------------===// +// +// Copyright 2018 Battelle Memorial Institute +// +// 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. +// +//===----------------------------------------------------------------------===// + +// Simple implementation of triangle counting, through graph pattern matching + +#include +#include +#include +#include +#include + +#include +#include "shad/core/algorithm.h" +#include "shad/data_structures/array.h" +#include "shad/extensions/graph_library/algorithms/jp_coloring.h" +#include "shad/extensions/graph_library/edge_index.h" +#include "shad/runtime/runtime.h" +#include "shad/util/measure.h" + +using graph_t = shad::EdgeIndex; + +// The GraphReader expects an input file in METIS dump format +graph_t::ObjectID GraphReader(std::ifstream &GFS) { + std::string line; + unsigned long EdgeNumber, VertexNumber; + + std::getline(GFS, line); + + std::istringstream headlineStream(line); + + headlineStream >> VertexNumber >> EdgeNumber; + EdgeNumber <<= 1; + + auto eiGraph = graph_t::Create(VertexNumber); + shad::rt::Handle handle; + + for (size_t i = 0L; i < VertexNumber; i++) { + size_t destination; + + std::getline(GFS, line); + std::istringstream lineStream(line); + std::vector edges; + while (!lineStream.eof()) { + lineStream >> destination; + destination--; + edges.push_back(destination); + } + eiGraph->AsyncInsertEdgeList(handle, i, edges.data(), edges.size()); + } + shad::rt::waitForCompletion(handle); + return eiGraph->GetGlobalID(); +} + +void printHelp(const std::string programName) { + std::cerr << "Usage: " << programName << " FILENAME" << std::endl; +} + +namespace shad { + +int main(int argc, char **argv) { + if (argc != 2) { + printHelp(argv[0]); + return -1; + } + + graph_t::ObjectID OID(-1); + auto loadingTime = shad::measure::duration([&]() { + // The GraphReader expects an input file in METIS dump format + std::ifstream inputFile; + inputFile.open(argv[1], std::ifstream::in); + OID = GraphReader(inputFile); + }); + std::cout << "Graph loaded in " << loadingTime.count() + << " seconds\nLet's start..." << std::endl; + auto eiPtr = graph_t::GetPtr(OID); + + size_t num_vertices = eiPtr->Size(); + std::cout << "NumVertices: " << num_vertices + << " Num Edges: " << eiPtr->NumEdges() << std::endl; + shad::Array::SharedPtr result; + LIKWID_MARKER_REGISTER("foo"); + LIKWID_MARKER_START("foo"); + auto duration = shad::measure::duration( + [&]() { result = jp_coloring(OID, num_vertices); }); + LIKWID_MARKER_STOP("foo"); + if (shad::rt::thisLocality() == shad::rt::Locality(0)) { + for (auto i = 0; i < num_vertices; ++i) { + std::cout << i << " " << result->At(i) << "\n"; + } + + auto c = + *(shad::max_element(result->begin(), result->end(), std::less<>())) + 1; + std::cout << "Num colors " << c << "\n"; + } + + std::cout << "Success " << duration.count() << " seconds" << std::endl; + + graph_t::Destroy(OID); + shad::Array::Destroy(result->GetGlobalID()); + return 0; +} + +} // namespace shad diff --git a/include/shad/extensions/graph_library/algorithms/jp_coloring.h b/include/shad/extensions/graph_library/algorithms/jp_coloring.h new file mode 100644 index 00000000..3fe0743b --- /dev/null +++ b/include/shad/extensions/graph_library/algorithms/jp_coloring.h @@ -0,0 +1,144 @@ +#ifndef INCLUDE_SHAD_EXTENSIONS_GRAPH_LIBRARY_ALGORITHMS_JP_COVER_H_ +#define INCLUDE_SHAD_EXTENSIONS_GRAPH_LIBRARY_ALGORITHMS_JP_COVER_H_ + +#include +#include + +#include "shad/data_structures/array.h" +#include "shad/extensions/graph_library/edge_index.h" +#include "shad/runtime/runtime.h" + +// ------------------------- +// Helper: deterministic-ish hash for priority (no RNG needed) +// ------------------------- +static inline uint32_t hash32(uint32_t x) { + x ^= x >> 16; + x *= 0x7feb352dU; + x ^= x >> 15; + x *= 0x846ca68bU; + x ^= x >> 16; + return x; +} + +// Neighbor scan to find if any UNCOLORED neighbor beats v in priority. +template +void jp_check_neighbor(shad::rt::Handle & /*handle*/, const VertexT &v, + const VertexT &nbr, + shad::Array::ObjectID &colorID, + shad::Array::ObjectID &isWinnerID) { + auto color = shad::Array::GetPtr(colorID); + if (color->At(v) >= 0) return; // v already colored + if (color->At(nbr) >= 0) return; // only compare vs uncolored neighbors + + const uint32_t pv = hash32(static_cast(v)); + const uint32_t pn = hash32(static_cast(nbr)); + + // Tie-break by vertex id to be deterministic + if (pn > pv || (pn == pv && nbr > v)) { + auto win = shad::Array::GetPtr(isWinnerID); + win->InsertAt(v, 0); // v is NOT a winner + } +} + +// For each vertex: tentatively mark winner=1, then scan neighbors to +// disqualify. +template +void jp_pick_winners(shad::rt::Handle &handle, const VertexT &v, + typename GraphT::ObjectID &gid, + shad::Array::ObjectID &colorID, + shad::Array::ObjectID &isWinnerID) { + auto color = shad::Array::GetPtr(colorID); + if (color->At(v) >= 0) return; + + auto win = shad::Array::GetPtr(isWinnerID); + win->InsertAt(v, 1); // tentatively winner + + auto g = GraphT::GetPtr(gid); + g->AsyncForEachNeighbor(handle, v, jp_check_neighbor, colorID, + isWinnerID); +} + +struct Scratch { + std::vector seen; +}; + +// A simple (slow but correct) color choice: scan neighbors, mark forbidden +// colors in a small local bitmap. You can optimize later (see section 6). +template +void jp_color_winner_neighbor(const VertexT &v, const VertexT &nbr, + shad::Array::ObjectID &colorID, + Scratch *&sp) { + auto color = shad::Array::GetPtr(colorID); + int32_t cn = color->At(nbr); + if (cn >= 0) sp->seen.push_back(cn); +} + +template +void jp_color_winners(shad::rt::Handle &handle, const VertexT &v, + typename GraphT::ObjectID &gid, + shad::Array::ObjectID &colorID, + shad::Array::ObjectID &isWinnerID) { + auto color = shad::Array::GetPtr(colorID); + if (color->At(v) >= 0) return; + + auto win = shad::Array::GetPtr(isWinnerID); + if (win->At(v) == 0) return; // not a winner this round + + Scratch s; + Scratch *sp = &s; + + auto g = GraphT::GetPtr(gid); + // TODO does not work with multi node, potentially a vertex could be on + // another node + g->ForEachNeighbor(v, jp_color_winner_neighbor, colorID, sp); + + auto &u = s.seen; + std::sort(u.begin(), u.end()); + u.erase(std::unique(u.begin(), u.end()), u.end()); + + int32_t mex = 0; + for (int32_t c : u) { + if (c == mex) + ++mex; + else if (c > mex) + break; + } + color->InsertAt(v, mex); + u.clear(); +} + +// Public API: returns vertex->color (clique id) on conflict graph => clique +// cover on compatibility graph. +template +shad::Array::SharedPtr jp_coloring(typename GraphT::ObjectID gid, + size_t num_vertices) { + auto color = shad::Array::Create(num_vertices, -1); + auto win = shad::Array::Create(num_vertices, 0); + + auto colorID = color->GetGlobalID(); + auto winID = win->GetGlobalID(); + + auto g = GraphT::GetPtr(gid); + + // Very simple termination: loop fixed rounds until no -1 remains would + // require a reduction; start with a conservative upper bound for a first + // version. + const size_t max_rounds = num_vertices; // refine later + for (size_t r = 0; r < max_rounds; ++r) { + shad::rt::Handle h1; + g->AsyncForEachVertex(h1, jp_pick_winners, gid, colorID, + winID); + shad::rt::waitForCompletion(h1); + + shad::rt::Handle h2; + g->AsyncForEachVertex(h2, jp_color_winners, gid, colorID, + winID); + shad::rt::waitForCompletion(h2); + } + + shad::Array::Destroy(win->GetGlobalID()); + + return color; +} + +#endif // INCLUDE_SHAD_EXTENSIONS_GRAPH_LIBRARY_ALGORITHMS_JP_COVER_H_ diff --git a/include/shad/extensions/graph_library/algorithms/sssp.h b/include/shad/extensions/graph_library/algorithms/sssp.h index c2aacb61..97d38f2a 100644 --- a/include/shad/extensions/graph_library/algorithms/sssp.h +++ b/include/shad/extensions/graph_library/algorithms/sssp.h @@ -125,12 +125,14 @@ size_t sssp_length(typename GraphT::ObjectID gid, VertexT src, VertexT dest) { auto q1Ptr = shad::Set::Create(num_vertices / 2); auto visited = shad::Array::Create(num_vertices, false); auto found = shad::Array::Create(1, false); - return __sssp_length(gid, num_vertices, q0Ptr, q1Ptr, - visited, found, src, dest); + shad::Set::Destroy(q0Ptr->GetGlobalID()); shad::Set::Destroy(q1Ptr->GetGlobalID()); shad::Array::Destroy(visited->GetGlobalID()); shad::Array::Destroy(found->GetGlobalID()); + + return __sssp_length(gid, num_vertices, q0Ptr, q1Ptr, + visited, found, src, dest); } #endif // INCLUDE_SHAD_EXTENSIONS_GRAPH_LIBRARY_ALGORITHMS_SSSP_H_ diff --git a/include/shad/extensions/graph_library/edge_index.h b/include/shad/extensions/graph_library/edge_index.h index fd1e9680..64d511e8 100755 --- a/include/shad/extensions/graph_library/edge_index.h +++ b/include/shad/extensions/graph_library/edge_index.h @@ -237,7 +237,7 @@ class EdgeIndex /// @param function The function to apply. /// @param args The function arguments. template - void ForEachNeighbor(const SrcT &src, ApplyFunT &&function, Args &... args); + void ForEachNeighbor(const SrcT &src, ApplyFunT &&function, Args &...args); /// @brief Asynchronously apply a user-defined function /// to each neighbor of a given vertex. @@ -258,7 +258,7 @@ class EdgeIndex /// @param args The function arguments. template void AsyncForEachNeighbor(rt::Handle &handle, const SrcT &src, - ApplyFunT &&function, Args &... args); + ApplyFunT &&function, Args &...args); /// @brief Apply a user-defined function to each vertex. /// @@ -272,7 +272,7 @@ class EdgeIndex /// @param function The function to apply. /// @param args The function arguments. template - void ForEachVertex(ApplyFunT &&function, Args &... args); + void ForEachVertex(ApplyFunT &&function, Args &...args); /// @brief Apply a user-defined function to each vertex. /// @@ -291,7 +291,7 @@ class EdgeIndex /// @param args The function arguments. template void AsyncForEachVertex(rt::Handle &handle, ApplyFunT &&function, - Args &... args); + Args &...args); /// @brief Apply a user-defined function to each edge. /// @@ -305,7 +305,7 @@ class EdgeIndex /// @param function The function to apply. /// @param args The function arguments. template - void ForEachEdge(ApplyFunT &&function, Args &... args); + void ForEachEdge(ApplyFunT &&function, Args &...args); /// @brief Asynchronously apply a user-defined function /// to each edge. @@ -325,7 +325,7 @@ class EdgeIndex /// @param args The function arguments. template void AsyncForEachEdge(rt::Handle &handle, ApplyFunT &&function, - Args &... args); + Args &...args); // FIXME for testing purposes only LocalEdgeIndex *GetLocalIndexPtr() { @@ -354,7 +354,7 @@ class EdgeIndex /// @param args The function arguments. template void VertexAttributesApply(const SrcT &src, ApplyFunT &&function, - Args &... args); + Args &...args); private: ObjectID oid_; @@ -677,7 +677,7 @@ inline void EdgeIndex::BufferedAsyncInsert( template template void EdgeIndex::ForEachVertex(ApplyFunT &&function, - Args &... args) { + Args &...args) { using FunctionTy = void (*)(const SrcT &src, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; @@ -696,8 +696,8 @@ template template void EdgeIndex::AsyncForEachVertex(rt::Handle &handle, ApplyFunT &&function, - Args &... args) { - using FunctionTy = void (*)(rt::Handle & h, const SrcT &src, Args &...); + Args &...args) { + using FunctionTy = void (*)(rt::Handle &h, const SrcT &src, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; feArgs arguments(oid_, fn, std::tuple(args...)); @@ -716,7 +716,7 @@ template template void EdgeIndex::ForEachNeighbor(const SrcT &src, ApplyFunT &&function, - Args &... args) { + Args &...args) { size_t targetId = shad::hash{}(src) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { @@ -741,14 +741,14 @@ void EdgeIndex::ForEachNeighbor(const SrcT &src, template template void EdgeIndex::AsyncForEachNeighbor( - rt::Handle &handle, const SrcT &src, ApplyFunT &&function, Args &... args) { + rt::Handle &handle, const SrcT &src, ApplyFunT &&function, Args &...args) { size_t targetId = shad::hash{}(src) % rt::numLocalities(); rt::Locality targetLocality(targetId); if (targetLocality == rt::thisLocality()) { localIndex_.AsyncForEachNeighbor(handle, src, function, args...); return; } - using FunctionTy = void (*)(rt::Handle & handle, const SrcT &src, + using FunctionTy = void (*)(rt::Handle &handle, const SrcT &src, const DestT &dest, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; @@ -767,7 +767,7 @@ void EdgeIndex::AsyncForEachNeighbor( template template void EdgeIndex::ForEachEdge(ApplyFunT &&function, - Args &... args) { + Args &...args) { using FunctionTy = void (*)(const SrcT &src, const DestT &dest, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; @@ -786,8 +786,8 @@ template template void EdgeIndex::AsyncForEachEdge(rt::Handle &handle, ApplyFunT &&function, - Args &... args) { - using FunctionTy = void (*)(rt::Handle & handle, const SrcT &src, + Args &...args) { + using FunctionTy = void (*)(rt::Handle &handle, const SrcT &src, const DestT &dest, Args &...); FunctionTy fn = std::forward(function); using feArgs = std::tuple>; @@ -830,7 +830,7 @@ bool EdgeIndex::GetVertexAttributes( template template void EdgeIndex::VertexAttributesApply( - const SrcT &src, ApplyFunT &&function, Args &... args) { + const SrcT &src, ApplyFunT &&function, Args &...args) { size_t targetId = shad::hash{}(src) % rt::numLocalities(); rt::Locality targetLocality(targetId); diff --git a/pkgconfig/shad-backend.pc.in b/pkgconfig/shad-backend.pc.in new file mode 100644 index 00000000..4dba812f --- /dev/null +++ b/pkgconfig/shad-backend.pc.in @@ -0,0 +1,6 @@ +Name: shad-@RUNTIME_SYSTEM@ +Description: SHAD + @RUNTIME_SYSTEM@ runtime +Version: @PROJECT_VERSION@ + +Requires: @PKG_REQUIRES@ shad-runtime-@RUNTIME_SYSTEM@ shad + diff --git a/pkgconfig/shad-runtime.pc.in b/pkgconfig/shad-runtime.pc.in new file mode 100644 index 00000000..d4d80966 --- /dev/null +++ b/pkgconfig/shad-runtime.pc.in @@ -0,0 +1,8 @@ +Name: shad-runtime-@RUNTIME_SYSTEM@ +Description: SHAD runtime backend (@RUNTIME_SYSTEM@) +Version: @PROJECT_VERSION@ + +Cflags: -DHAVE_@SHAD_RUNTIME_SYSTEM@=1 + +# The runtime libs this backend provides +Libs: -l:lib@RUNTIME_SYSTEM@_runtime.a diff --git a/pkgconfig/shad.pc.in b/pkgconfig/shad.pc.in index 6ba2bbc4..efa73795 100644 --- a/pkgconfig/shad.pc.in +++ b/pkgconfig/shad.pc.in @@ -1,10 +1,11 @@ prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} includedir=${prefix}/include -libdir=${exec_prefix}/lib +libdir=${prefix}/lib -Name: @CMAKE_PROJECT_NAME@ -Description: SHAD - Scalable and High-Performance Algorithms and Data-Structures. -Version: @PACKAGE_VERSION@ -Cflags: @CMAKE_CXX_FLAGS@ -D HAVE_@SHAD_RUNTIME_SYSTEM@=1 -I${includedir} -Libs: -L${libdir} -Wl,-rpath,${libdir} -lruntime -lutils @LINK_FLAGS@ +Name: shad +Description: SHAD - Scalable and High-Performance Algorithms and Data-Structures +Version: @PROJECT_VERSION@ + +Cflags: -I${includedir} +Libs: -L${libdir} @MPI_CXX_LINK_FLAGS@ -lmpi diff --git a/pkgconfig/shad_cpp_simple.pc.in b/pkgconfig/shad_cpp_simple.pc.in deleted file mode 100644 index 2ad88c29..00000000 --- a/pkgconfig/shad_cpp_simple.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib - -Name: @CMAKE_PROJECT_NAME@_CPP_SIMPLE -Description: SHAD - Scalable and High-Performance Algorithms and Data-Structures, CPP_SIMPLE Backend. -Version: @PACKAGE_VERSION@ -Cflags: @CMAKE_CXX_FLAGS@ -DHAVE_CPP_SIMPLE=1 -I${includedir} -Libs: -L${libdir} -Wl,-rpath,${libdir} -lcpp_simple_runtime -lutils @LINK_FLAGS@ diff --git a/pkgconfig/shad_gmt.pc.in b/pkgconfig/shad_gmt.pc.in deleted file mode 100644 index d1633ffa..00000000 --- a/pkgconfig/shad_gmt.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib -runtimeincludedirs=@GMT_INCLUDE_DIRS@ - -Name: @CMAKE_PROJECT_NAME@_GMT -Description: SHAD - Scalable and High-Performance Algorithms and Data-Structures, GMT Backend. -Version: @PACKAGE_VERSION@ -Cflags: @CMAKE_CXX_FLAGS@ -DHAVE_GMT=1 -I${includedir} -I${runtimeincludedirs} -Libs: -L${libdir} -Wl,-rpath,${libdir} -lgmt_runtime -lutils @LINK_FLAGS@ diff --git a/pkgconfig/shad_tbb.pc.in b/pkgconfig/shad_tbb.pc.in deleted file mode 100644 index d1417256..00000000 --- a/pkgconfig/shad_tbb.pc.in +++ /dev/null @@ -1,10 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -includedir=${prefix}/include -libdir=${exec_prefix}/lib - -Name: @CMAKE_PROJECT_NAME@_TBB -Description: SHAD - Scalable and High-Performance Algorithms and Data-Structures, TBB Backend. -Version: @PACKAGE_VERSION@ -Cflags: @CMAKE_CXX_FLAGS@ -DHAVE_TBB=1 -I${includedir} -Libs: -L${libdir} -Wl,-rpath,${libdir} -ltbb_runtime -lutils @LINK_FLAGS@ diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index 26b00f15..b1ea15d2 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -39,14 +39,9 @@ foreach(rp ${runtime_prefixes}) target_link_libraries(${rp}_runtime PUBLIC ${GPERFTOOLS_LIBRARIES}) endif() string(TOUPPER "${rp}" uprp) - configure_file( - ${SHAD_MAIN_SRC_DIR}/pkgconfig/shad_${rp}.pc.in - ${SHAD_BINARY_DIR}/pkgconfig/shad_${rp}.pc @ONLY) - install(FILES ${SHAD_BINARY_DIR}/pkgconfig/shad_${rp}.pc - DESTINATION pkgconfig) - + target_include_directories(${rp}_runtime - PUBLIC + PUBLIC ${${uprp}_INCLUDE_DIRS} $ $) @@ -58,6 +53,28 @@ foreach(rp ${runtime_prefixes}) install(EXPORT ${rp}_runtime_export FILE shad_${rp}_runtime_export.cmake DESTINATION cmake) + + if("${rp}" STREQUAL "gmt") + set(PKG_REQUIRES "gmt") + endif() + set(RUNTIME_SYSTEM ${rp}) + configure_file( + ${SHAD_MAIN_SRC_DIR}/pkgconfig/shad-runtime.pc.in + ${SHAD_BINARY_DIR}/pkgconfig/shad-runtime-${rp}.pc + @ONLY + ) + install(FILES ${SHAD_BINARY_DIR}/pkgconfig/shad-runtime-${rp}.pc + DESTINATION "pkgconfig") + + configure_file( + ${SHAD_MAIN_SRC_DIR}/pkgconfig/shad-backend.pc.in + ${SHAD_BINARY_DIR}/pkgconfig/shad-${rp}.pc + @ONLY +) + install(FILES ${SHAD_BINARY_DIR}/pkgconfig/shad-${rp}.pc + DESTINATION "pkgconfig") + + endforeach(rp)