Skip to content

Commit e94f380

Browse files
authored
Tag searcher CLI executable (#16)
* Command-line executable for running searches * WL tools for importing/exporting the custom file formats for crib, init, and result files * Dockerfile for building an Alpine-based image with the executable * .exe artifact in Windows build job
1 parent 135f11e commit e94f380

31 files changed

Lines changed: 1417 additions & 12 deletions

.circleci/config.yml

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ jobs:
6262
- run:
6363
name: Install Required Tools
6464
command: |
65-
apk add --no-cache bash git g++ make cmake clang py-pip shellcheck shfmt grep npm
65+
apk add --no-cache \
66+
bash boost-dev boost-static git g++ make cmake \
67+
clang py-pip shellcheck shfmt grep npm
6668
pip install cpplint
6769
npm install -g markdownlint-cli
6870
@@ -83,6 +85,7 @@ jobs:
8385
mkdir build
8486
cd build
8587
cmake .. -DPOST_TAG_SYSTEM_BUILD_TESTING=ON \
88+
-DPOST_TAG_SYSTEM_BUILD_CLI=ON \
8689
-DPOST_TAG_SYSTEM_ENABLE_ALLWARNINGS=ON
8790
cmake --build .
8891
@@ -105,14 +108,15 @@ jobs:
105108

106109
- run:
107110
name: Install Required Tools
108-
command: apk add --no-cache bash git g++ make cmake
111+
command: apk add --no-cache bash boost-dev boost-static git g++ make cmake
109112

110113
- run:
111114
name: Build
112115
command: |
113116
mkdir build
114117
cd build
115118
cmake .. -DPOST_TAG_SYSTEM_BUILD_TESTING=ON \
119+
-DPOST_TAG_SYSTEM_BUILD_CLI=ON \
116120
-DPOST_TAG_SYSTEM_ENABLE_ALLWARNINGS=ON
117121
cmake --build .
118122
@@ -170,6 +174,19 @@ jobs:
170174
cmakeDir=$(dir -1 | findstr -i cmake-*)
171175
echo "export PATH=\"$(pwd)/$cmakeDir/bin:$PATH\"" >> $BASH_ENV
172176
177+
- run:
178+
name: Install Boost headers and build libraries
179+
command: |
180+
boostURL="https://dl.bintray.com/boostorg/release/1.72.0/source/boost_1_72_0.zip"
181+
curl -L --output boost.zip $boostURL
182+
unzip -q boost.zip
183+
boostDir=$(dir -1 | findstr -i boost_*)
184+
echo "export BOOST_ROOT=\"$(pwd)/$boostDir\"" >> $BASH_ENV
185+
grep BOOST_ $BASH_ENV
186+
cd $boostDir
187+
./bootstrap.bat
188+
./b2 --with-program_options
189+
173190
- run:
174191
name: Build
175192
command: scripts/buildLibraryResources.sh
@@ -182,6 +199,22 @@ jobs:
182199
- store_artifacts:
183200
path: ./LibraryResources/
184201

202+
- run:
203+
name: Build
204+
command: |
205+
rm -rf build
206+
mkdir -p build
207+
cd build
208+
cmake .. \
209+
-DPOST_TAG_SYSTEM_ENABLE_ALLWARNINGS=ON \
210+
-DPOST_TAG_SYSTEM_BUILD_CLI=ON \
211+
-DPOST_TAG_SYSTEM_CLI_STATIC_BUILD=ON \
212+
-DCMAKE_BUILD_TYPE=Release
213+
cmake --build . --config Release
214+
215+
- store_artifacts:
216+
path: ./build/CLI/Release
217+
185218
workflows:
186219
version: 2
187220
build-and-test:

.dockerignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build/
2+
*.xcodeproj/

CLI/CMakeLists.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
option(POST_TAG_SYSTEM_CLI_STATIC_BUILD "Build command-line interface as a statically linked executable." OFF)
2+
3+
if(POST_TAG_SYSTEM_CLI_STATIC_BUILD)
4+
set(Boost_USE_STATIC_LIBS ON)
5+
endif()
6+
7+
find_package(Boost 1.67
8+
COMPONENTS program_options
9+
REQUIRED)
10+
11+
set(_link_libraries
12+
PostTagSystem
13+
${Boost_LIBRARIES}
14+
)
15+
16+
add_executable(wolfram-postproject
17+
main.cpp arguments.cpp
18+
files/PostTagFileReader.cpp files/PostTagFileWriter.cpp
19+
files/PostTagCribFile.cpp files/PostTagInitFile.cpp
20+
files/PostTagResultFile.cpp
21+
)
22+
23+
target_include_directories(wolfram-postproject PRIVATE
24+
${Boost_INCLUDE_DIR})
25+
26+
target_compile_options(wolfram-postproject PRIVATE
27+
${POST_TAG_SYSTEM_COMPILE_OPTIONS})
28+
29+
target_compile_definitions(wolfram-postproject PRIVATE
30+
POST_TAG_VERSION_MAJOR=${CMAKE_PROJECT_VERSION_MAJOR}
31+
POST_TAG_VERSION_MINOR=${CMAKE_PROJECT_VERSION_MINOR}
32+
POST_TAG_VERSION_PATCH=${CMAKE_PROJECT_VERSION_PATCH}
33+
)
34+
35+
if(POST_TAG_SYSTEM_CLI_STATIC_BUILD)
36+
target_link_options(wolfram-postproject PUBLIC "-static")
37+
endif()
38+
39+
target_link_libraries(wolfram-postproject ${_link_libraries})

CLI/arguments.cpp

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include "arguments.hpp"
2+
3+
#include <iostream>
4+
#include <string>
5+
6+
namespace po = boost::program_options;
7+
8+
auto validator_uint_greater_equal(const char* const option_name, uint64_t min) {
9+
return [option_name, min](auto n) {
10+
if (n < min) {
11+
throw po::validation_error(
12+
po::validation_error::invalid_option_value, option_name, std::to_string(static_cast<uint64_t>(n)));
13+
}
14+
};
15+
}
16+
17+
auto validator_uint_between(const char* const option_name, uint64_t min, uint64_t max) {
18+
return [option_name, min, max](auto n) {
19+
if (n < min || n > max) {
20+
throw po::validation_error(
21+
po::validation_error::invalid_option_value, option_name, std::to_string(static_cast<uint64_t>(n)));
22+
}
23+
};
24+
}
25+
26+
void validate_option_existence(const po::variables_map& args, const char* const option_name) {
27+
if (args.count(option_name) == 0) {
28+
// TODO(jessef): better exception
29+
throw po::validation_error(po::validation_error::at_least_one_value_required, option_name);
30+
}
31+
}
32+
33+
void mode_validate_chase(const po::variables_map& args) {
34+
validate_option_existence(args, "initsize");
35+
validate_option_existence(args, "initstart");
36+
validate_option_existence(args, "initcount");
37+
}
38+
39+
void mode_validate_pounce(const po::variables_map& args) { validate_option_existence(args, "initfile"); }
40+
41+
po::variables_map parse_arguments(int argc, char** argv) {
42+
po::options_description general_options("General options");
43+
po::options_description chase_options("Chase-mode options");
44+
po::options_description pounce_options("Pounce-mode options");
45+
46+
// clang-format off
47+
general_options.add_options()
48+
("help,h", po::bool_switch(),
49+
"Print help text")
50+
("version,v", po::bool_switch(),
51+
"Print program version")
52+
("chase,c", po::bool_switch(),
53+
"Chase mode (range search)")
54+
("pounce,p", po::bool_switch(),
55+
"Pounce mode (file search)")
56+
("outfile,o", po::value<std::string>()->default_value("./output.postresult")->value_name("path.postresult"),
57+
"Path to output file")
58+
("maxsize,x", po::value<uint64_t>()->default_value(static_cast<uint64_t>(1e9), "10^9")->value_name("size"),
59+
"Maximum tape length to evaluate each initial condition to (0 = no limit)")
60+
("maxoutsize,z", po::value<uint64_t>()->value_name("size"),
61+
"Maximum tape length to include in output file entries (0 = never write final tapes; omit for no limit)")
62+
("maxsteps,m", po::value<uint64_t>()->default_value(static_cast<uint64_t>(1e10), "10^10")->value_name("steps"),
63+
"Maximum number of steps to evaluate each initial condition to (0 = no limit)")
64+
("timeout,t", po::value<uint32_t>()->default_value(0)->value_name("secs"),
65+
"Total execution time constraint (seconds) (0 = no limit)");
66+
67+
chase_options.add_options()
68+
("cribfile,f", po::value<std::string>()->value_name("path.postcrib"),
69+
"Path to crib file (list of known sequences)")
70+
("initsize,l", po::value<uint16_t>()->value_name("size")
71+
->notifier(validator_uint_between("initsize", 1, 64)),
72+
"Size of initial condition tapes")
73+
("initstart,s", po::value<uint64_t>()->value_name("start"),
74+
"Starting initial condition tape (as decimal integer)")
75+
("initcount,n", po::value<uint64_t>()->default_value(1)->value_name("count")
76+
->notifier(validator_uint_greater_equal("initcount", 1)),
77+
"Number of initial condition tapes to evaluate")
78+
("initoffset,e", po::value<uint64_t>()->default_value(0)->value_name("offset"),
79+
"Shifts starting condition by offset * count (for use with zero-indexed array jobs)");
80+
81+
pounce_options.add_options()
82+
("initfile,i", po::value<std::string>()->value_name("path.postinit"),
83+
"Path to file of initial conditions");
84+
// clang-format on
85+
86+
po::options_description all_options;
87+
all_options.add(general_options).add(chase_options).add(pounce_options);
88+
89+
po::variables_map args;
90+
po::store(po::command_line_parser(argc, argv).options(all_options).run(), args);
91+
po::notify(args);
92+
93+
if (args["help"].as<bool>()) {
94+
std::cout << all_options << "\n";
95+
return args;
96+
}
97+
98+
if (args["version"].as<bool>()) {
99+
#if defined(POST_TAG_VERSION_MAJOR) && defined(POST_TAG_VERSION_MINOR) && defined(POST_TAG_VERSION_PATCH)
100+
printf("wolfram-postproject v%u.%u.%u =^._.^=\n",
101+
POST_TAG_VERSION_MAJOR,
102+
POST_TAG_VERSION_MINOR,
103+
POST_TAG_VERSION_PATCH);
104+
#else
105+
printf("wolfram-postproject unknown version =^._.^=\n");
106+
#endif
107+
108+
return args;
109+
}
110+
111+
if (args["chase"].as<bool>() && args["pounce"].as<bool>()) {
112+
throw std::runtime_error("Only one of --chase or --pounce may be specified");
113+
} else if (args["chase"].as<bool>()) {
114+
mode_validate_chase(args);
115+
} else if (args["pounce"].as<bool>()) {
116+
mode_validate_pounce(args);
117+
} else {
118+
throw std::runtime_error("One of --chase or --pounce must be specified");
119+
}
120+
121+
return args;
122+
}

CLI/arguments.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef CLI_ARGUMENTS_HPP_
2+
#define CLI_ARGUMENTS_HPP_
3+
4+
#include "boost/program_options.hpp"
5+
6+
boost::program_options::variables_map parse_arguments(int argc, char** argv);
7+
8+
#endif // CLI_ARGUMENTS_HPP_

CLI/files/PostTagCribFile.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include "PostTagCribFile.hpp"
2+
3+
#include "boost/format.hpp"
4+
5+
using PostTagSystem::TagState;
6+
7+
PostTagCribFile PostTagCribFileReader::read_file() {
8+
uint8_t file_magic = read_u8();
9+
PostTagFileMagic format_magic = CribFileMagic;
10+
11+
if (file_magic != format_magic) {
12+
throw std::runtime_error((boost::format("File magic number 0x%X did not match expected 0x%X") %
13+
static_cast<unsigned int>(file_magic) % static_cast<unsigned int>(format_magic))
14+
.str());
15+
}
16+
17+
uint8_t version = read_u8();
18+
switch (version) {
19+
case Version1:
20+
return read_file_V1();
21+
22+
default:
23+
throw std::runtime_error(
24+
(boost::format("Unsupported file version %u") % static_cast<unsigned int>(version)).str());
25+
}
26+
}
27+
28+
PostTagCribFile PostTagCribFileReader::read_file_V1() {
29+
PostTagCribFile file;
30+
file.version = Version1;
31+
file.checkpoint_count = read_u64();
32+
file.checkpoints = read_checkpoints(file.checkpoint_count);
33+
34+
return file;
35+
}
36+
37+
std::vector<TagState> PostTagCribFileReader::read_checkpoints(uint64_t checkpoint_count) {
38+
std::vector<TagState> checkpoints(checkpoint_count);
39+
for (size_t i = 0; i < checkpoint_count; i++) {
40+
checkpoints[i].headState = read_u8();
41+
checkpoints[i].tape = read_prefixed_bits();
42+
}
43+
44+
return checkpoints;
45+
}

CLI/files/PostTagCribFile.hpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef CLI_FILES_POSTTAGCRIBFILE_HPP_
2+
#define CLI_FILES_POSTTAGCRIBFILE_HPP_
3+
4+
#include <fstream>
5+
#include <vector>
6+
7+
#include "PostTagFile.hpp"
8+
#include "PostTagFileReader.hpp"
9+
#include "TagState.hpp"
10+
11+
struct PostTagCribFile {
12+
PostTagFileVersion version;
13+
14+
uint64_t checkpoint_count;
15+
std::vector<PostTagSystem::TagState> checkpoints;
16+
};
17+
18+
class PostTagCribFileReader : public PostTagFileReader {
19+
public:
20+
using PostTagFileReader::PostTagFileReader;
21+
22+
PostTagCribFile read_file();
23+
24+
private:
25+
PostTagCribFile read_file_V1();
26+
27+
std::vector<PostTagSystem::TagState> read_checkpoints(uint64_t checkpoint_count);
28+
};
29+
30+
#endif // CLI_FILES_POSTTAGCRIBFILE_HPP_

CLI/files/PostTagFile.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef CLI_FILES_POSTTAGFILE_HPP_
2+
#define CLI_FILES_POSTTAGFILE_HPP_
3+
4+
enum PostTagFileVersion : uint8_t { Version1 = 1 };
5+
6+
enum PostTagFileMagic : uint8_t { CribFileMagic = 'C', InitFileMagic = 'I', ResultFileMagic = 'R' };
7+
8+
#endif // CLI_FILES_POSTTAGFILE_HPP_

0 commit comments

Comments
 (0)