fuzzprep 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. fuzzprep/__init__.py +3 -0
  2. fuzzprep/__main__.py +4 -0
  3. fuzzprep/agents/harness_builder/SKILL.md +156 -0
  4. fuzzprep/agents/library_builder/SKILL.md +147 -0
  5. fuzzprep/agents/scripts/check_build.sh +87 -0
  6. fuzzprep/agents/scripts/check_build_in_container.sh +73 -0
  7. fuzzprep/agents/scripts/check_dockerfile_from_scratch.sh +33 -0
  8. fuzzprep/cli.py +1109 -0
  9. fuzzprep/core/__init__.py +0 -0
  10. fuzzprep/core/agent_stream.py +304 -0
  11. fuzzprep/core/files.py +35 -0
  12. fuzzprep/core/paths.py +25 -0
  13. fuzzprep/core/reporting.py +218 -0
  14. fuzzprep/core/repos.py +217 -0
  15. fuzzprep/core/resources.py +41 -0
  16. fuzzprep/core/subprocesses.py +197 -0
  17. fuzzprep/feature_extractor/__init__.py +0 -0
  18. fuzzprep/feature_extractor/benchmark_yaml.py +92 -0
  19. fuzzprep/feature_extractor/extraction.py +184 -0
  20. fuzzprep/feature_extractor/models.py +96 -0
  21. fuzzprep/feature_extractor/native/.clang-format +1 -0
  22. fuzzprep/feature_extractor/native/CMakeLists.txt +90 -0
  23. fuzzprep/feature_extractor/native/include/feature_extractor.hpp +136 -0
  24. fuzzprep/feature_extractor/native/src/extraction_action.cpp +294 -0
  25. fuzzprep/feature_extractor/native/src/json_writer.cpp +154 -0
  26. fuzzprep/feature_extractor/native/src/macro_callbacks.cpp +75 -0
  27. fuzzprep/feature_extractor/native/src/main.cpp +130 -0
  28. fuzzprep/feature_extractor/native_build.py +99 -0
  29. fuzzprep/library_builder/__init__.py +0 -0
  30. fuzzprep/library_builder/agents.py +472 -0
  31. fuzzprep/library_builder/analysis.py +145 -0
  32. fuzzprep/library_builder/build_parameters.py +158 -0
  33. fuzzprep/library_builder/dependency_resolution.py +139 -0
  34. fuzzprep/library_builder/environments/__init__.py +0 -0
  35. fuzzprep/library_builder/environments/base.py +89 -0
  36. fuzzprep/library_builder/environments/gate.py +96 -0
  37. fuzzprep/library_builder/environments/local.py +205 -0
  38. fuzzprep/library_builder/environments/oss_fuzz.py +485 -0
  39. fuzzprep/library_builder/environments/verification.py +125 -0
  40. fuzzprep/library_builder/exploration.py +217 -0
  41. fuzzprep/library_builder/generation.py +325 -0
  42. fuzzprep/library_builder/harness_explorer.py +257 -0
  43. fuzzprep/library_builder/models.py +169 -0
  44. fuzzprep/library_builder/package_names.json +33 -0
  45. fuzzprep/library_builder/package_names.py +40 -0
  46. fuzzprep/library_builder/scripts.py +389 -0
  47. fuzzprep/library_builder/stats.py +102 -0
  48. fuzzprep/library_builder/symbol_patterns.json +65 -0
  49. fuzzprep/library_builder/timeouts.py +14 -0
  50. fuzzprep/library_builder/workspace.py +250 -0
  51. fuzzprep-0.1.0.dist-info/METADATA +255 -0
  52. fuzzprep-0.1.0.dist-info/RECORD +56 -0
  53. fuzzprep-0.1.0.dist-info/WHEEL +4 -0
  54. fuzzprep-0.1.0.dist-info/entry_points.txt +3 -0
  55. fuzzprep-0.1.0.dist-info/licenses/LICENSE +202 -0
  56. fuzzprep-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +52 -0
@@ -0,0 +1,184 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from fuzzprep.feature_extractor.models import (
7
+ SCHEMA_VERSION,
8
+ EnumDefinition,
9
+ Enumerator,
10
+ FeatureArtifactSet,
11
+ Field,
12
+ FunctionSignature,
13
+ MacroDefinition,
14
+ Param,
15
+ StructUnionDefinition,
16
+ Typedef,
17
+ )
18
+ from fuzzprep.library_builder.models import Language
19
+
20
+ _REQUIRED_FIELDS = (
21
+ "schema_version",
22
+ "project_name",
23
+ "language",
24
+ "functions",
25
+ "typedefs",
26
+ "macros",
27
+ "enums",
28
+ "records",
29
+ "warnings",
30
+ )
31
+
32
+ _LANGUAGE_BY_VALUE = {lang.value: lang for lang in Language}
33
+
34
+
35
+ class FeatureArtifactError(Exception):
36
+ """The feature artifact JSON is missing, malformed, or from an incompatible schema."""
37
+
38
+
39
+ class MissingCompileCommandsError(Exception):
40
+ """The output directory has no compile_commands.json to extract features from."""
41
+
42
+
43
+ class MissingFeatureArtifactError(Exception):
44
+ """The output directory has no features.json; extract-features must run first."""
45
+
46
+
47
+ def extract_features(output_dir: Path) -> FeatureArtifactSet:
48
+ """Extract a library's declarations into features.json, returning the parsed result.
49
+
50
+ Reads <output_dir>/compile_commands.json, builds and invokes the native tool, and
51
+ overwrites <output_dir>/features.json with the result.
52
+ """
53
+ from fuzzprep.core.subprocesses import run_command_streaming
54
+ from fuzzprep.feature_extractor.native_build import build_native_tool
55
+
56
+ compile_commands = output_dir / "compile_commands.json"
57
+ if not compile_commands.is_file():
58
+ raise MissingCompileCommandsError(
59
+ f"{compile_commands} not found. Generate one with CMake "
60
+ "(-DCMAKE_EXPORT_COMPILE_COMMANDS=ON) or by wrapping a non-CMake build "
61
+ "with 'bear -- <build command>'."
62
+ )
63
+
64
+ binary = build_native_tool()
65
+ features_json = output_dir / "features.json"
66
+ project_name = output_dir.resolve().name
67
+ cmd = [str(binary.resolve()), str(output_dir.resolve()), str(features_json), project_name]
68
+ result = run_command_streaming(
69
+ cmd,
70
+ Path.cwd(),
71
+ timeout=600,
72
+ )
73
+ if result.exit_code != 0:
74
+ raise FeatureArtifactError(
75
+ f"Native feature_extractor tool failed\n"
76
+ "Run below command to reproduce:\n"
77
+ f"cd {Path.cwd()} && "
78
+ f"{' '.join(cmd)}"
79
+ )
80
+ features_json.write_text(json.dumps(json.loads(features_json.read_text()), indent=2) + "\n")
81
+ return load_feature_artifact(features_json)
82
+
83
+
84
+ def load_feature_artifact(path: Path) -> FeatureArtifactSet:
85
+ """Parse and validate a features.json file into a typed FeatureArtifactSet.
86
+
87
+ Raises FeatureArtifactError with an actionable message on a missing file,
88
+ malformed JSON, missing/mistyped fields, or a schema_version mismatch.
89
+ """
90
+ if not path.exists():
91
+ raise FeatureArtifactError(f"{path} does not exist. Run 'fuzzprep extract-features' first.")
92
+ try:
93
+ data = json.loads(path.read_text())
94
+ except json.JSONDecodeError as exc:
95
+ raise FeatureArtifactError(f"{path} is not valid JSON: {exc}") from exc
96
+
97
+ if not isinstance(data, dict):
98
+ raise FeatureArtifactError(f"{path} does not contain a JSON object.")
99
+
100
+ project_name, language = _validate_header_fields(path, data)
101
+
102
+ try:
103
+ return FeatureArtifactSet(
104
+ schema_version=data["schema_version"],
105
+ project_name=project_name,
106
+ language=language,
107
+ functions=[_parse_function(f) for f in data["functions"]],
108
+ typedefs=[_parse_typedef(t) for t in data["typedefs"]],
109
+ macros=[_parse_macro(m) for m in data["macros"]],
110
+ enums=[_parse_enum(e) for e in data["enums"]],
111
+ records=[_parse_record(r) for r in data["records"]],
112
+ warnings=[str(w) for w in data["warnings"]],
113
+ )
114
+ except (KeyError, TypeError) as exc:
115
+ raise FeatureArtifactError(f"{path} has a malformed entry: {exc}") from exc
116
+
117
+
118
+ def _validate_header_fields(path: Path, data: dict) -> tuple[str, Language]:
119
+ """Validate schema_version/project_name/language, returning the latter two."""
120
+ missing = [key for key in _REQUIRED_FIELDS if key not in data]
121
+ if missing:
122
+ raise FeatureArtifactError(f"{path} is missing required field(s): {', '.join(missing)}")
123
+
124
+ if data["schema_version"] != SCHEMA_VERSION:
125
+ raise FeatureArtifactError(
126
+ f"{path} has schema_version {data['schema_version']!r}, "
127
+ f"expected {SCHEMA_VERSION}. Re-run 'fuzzprep extract-features'."
128
+ )
129
+
130
+ project_name = data["project_name"]
131
+ if not isinstance(project_name, str) or not project_name:
132
+ raise FeatureArtifactError(f"{path} has an empty or non-string project_name.")
133
+
134
+ language = _LANGUAGE_BY_VALUE.get(data["language"])
135
+ if language is None:
136
+ raise FeatureArtifactError(f"{path} has an unrecognized language: {data['language']!r}")
137
+
138
+ return project_name, language
139
+
140
+
141
+ def _parse_function(data: dict) -> FunctionSignature:
142
+ return FunctionSignature(
143
+ name=data["name"],
144
+ return_type=data["return_type"],
145
+ params=[Param(name=p["name"], type=p["type"]) for p in data["params"]],
146
+ signature=data["signature"],
147
+ is_public_api=data["is_public_api"],
148
+ header_path=data["header_path"],
149
+ )
150
+
151
+
152
+ def _parse_typedef(data: dict) -> Typedef:
153
+ return Typedef(
154
+ name=data["name"],
155
+ underlying_type=data["underlying_type"],
156
+ header_path=data["header_path"],
157
+ )
158
+
159
+
160
+ def _parse_macro(data: dict) -> MacroDefinition:
161
+ return MacroDefinition(
162
+ name=data["name"],
163
+ is_function_like=data["is_function_like"],
164
+ params=[str(p) for p in data["params"]],
165
+ value=data["value"],
166
+ header_path=data["header_path"],
167
+ )
168
+
169
+
170
+ def _parse_enum(data: dict) -> EnumDefinition:
171
+ return EnumDefinition(
172
+ name=data["name"],
173
+ enumerators=[Enumerator(name=e["name"], value=e["value"]) for e in data["enumerators"]],
174
+ header_path=data["header_path"],
175
+ )
176
+
177
+
178
+ def _parse_record(data: dict) -> StructUnionDefinition:
179
+ return StructUnionDefinition(
180
+ name=data["name"],
181
+ kind=data["kind"],
182
+ fields=[Field(name=f["name"], type=f["type"]) for f in data["fields"]],
183
+ header_path=data["header_path"],
184
+ )
@@ -0,0 +1,96 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ from fuzzprep.library_builder.models import Language
6
+
7
+ SCHEMA_VERSION = 1
8
+
9
+
10
+ @dataclass
11
+ class Param:
12
+ name: str
13
+ type: str
14
+
15
+
16
+ @dataclass
17
+ class FunctionSignature:
18
+ name: str
19
+ return_type: str
20
+ params: list[Param]
21
+ signature: str
22
+ is_public_api: bool
23
+ header_path: str
24
+
25
+
26
+ @dataclass
27
+ class Typedef:
28
+ name: str
29
+ underlying_type: str
30
+ header_path: str
31
+
32
+
33
+ @dataclass
34
+ class MacroDefinition:
35
+ name: str
36
+ is_function_like: bool
37
+ params: list[str]
38
+ value: str
39
+ header_path: str
40
+
41
+
42
+ @dataclass
43
+ class Enumerator:
44
+ name: str
45
+ value: int
46
+
47
+
48
+ @dataclass
49
+ class EnumDefinition:
50
+ name: str | None
51
+ enumerators: list[Enumerator]
52
+ header_path: str
53
+
54
+
55
+ @dataclass
56
+ class Field:
57
+ name: str
58
+ type: str
59
+
60
+
61
+ @dataclass
62
+ class StructUnionDefinition:
63
+ name: str | None
64
+ kind: str # "struct" | "union"
65
+ fields: list[Field]
66
+ header_path: str
67
+
68
+
69
+ @dataclass
70
+ class FeatureArtifactSet:
71
+ schema_version: int
72
+ project_name: str
73
+ language: Language
74
+ functions: list[FunctionSignature] = field(default_factory=list)
75
+ typedefs: list[Typedef] = field(default_factory=list)
76
+ macros: list[MacroDefinition] = field(default_factory=list)
77
+ enums: list[EnumDefinition] = field(default_factory=list)
78
+ records: list[StructUnionDefinition] = field(default_factory=list)
79
+ warnings: list[str] = field(default_factory=list)
80
+
81
+
82
+ @dataclass
83
+ class BenchmarkFunction:
84
+ name: str
85
+ signature: str
86
+ return_type: str
87
+ params: list[Param]
88
+
89
+
90
+ @dataclass
91
+ class BenchmarkYaml:
92
+ project: str
93
+ language: str
94
+ target_name: str
95
+ target_path: str
96
+ functions: list[BenchmarkFunction] = field(default_factory=list)
@@ -0,0 +1 @@
1
+ BasedOnStyle: LLVM
@@ -0,0 +1,90 @@
1
+ cmake_minimum_required(VERSION 3.20)
2
+ project(feature_extractor CXX C)
3
+
4
+ set(CMAKE_CXX_STANDARD 17)
5
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
6
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
7
+
8
+ if(NOT CMAKE_BUILD_TYPE)
9
+ set(CMAKE_BUILD_TYPE Release)
10
+ endif()
11
+
12
+ find_package(Clang REQUIRED CONFIG)
13
+ find_package(LLVM REQUIRED CONFIG)
14
+
15
+ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
16
+ message(STATUS "Using ClangConfig.cmake in: ${Clang_DIR}")
17
+
18
+ list(GET LLVM_LIBRARY_DIRS 0 _llvm_library_dir)
19
+ set(CLANG_RESOURCE_DIR "${_llvm_library_dir}/clang/${LLVM_VERSION_MAJOR}")
20
+ message(STATUS "Using clang resource dir: ${CLANG_RESOURCE_DIR}")
21
+
22
+ # -resource-dir only covers clang's own builtin headers (stddef.h, stdarg.h, ...) — on
23
+ # macOS, platform libc headers (inttypes.h, stdio.h, ...) live under the Xcode SDK and
24
+ # are found only via -isysroot, which main.cpp adds explicitly rather than relying on
25
+ # ClangTool's ad hoc driver invocation to auto-detect it.
26
+ set(CLANG_SYSROOT "")
27
+ if(APPLE)
28
+ execute_process(
29
+ COMMAND xcrun --show-sdk-path
30
+ OUTPUT_VARIABLE CLANG_SYSROOT
31
+ OUTPUT_STRIP_TRAILING_WHITESPACE
32
+ ERROR_QUIET
33
+ )
34
+ message(STATUS "Using macOS SDK sysroot: ${CLANG_SYSROOT}")
35
+ endif()
36
+
37
+ # cJSON is linked statically into feature_extractor, so its MIT notice has to travel with any
38
+ # redistributed binary. THIRD_PARTY_NOTICES.md at the repository root carries that notice —
39
+ # update it when this pin moves or another dependency is added here.
40
+ include(FetchContent)
41
+ set(ENABLE_CJSON_TEST OFF CACHE BOOL "" FORCE)
42
+ set(ENABLE_CJSON_UTILS OFF CACHE BOOL "" FORCE)
43
+ set(CJSON_OVERRIDE_BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
44
+ set(CJSON_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
45
+ FetchContent_Declare(
46
+ cjson
47
+ GIT_REPOSITORY https://github.com/DaveGamble/cJSON.git
48
+ GIT_TAG c859b25da02955fef659d658b8f324b5cde87be3 # v1.7.19
49
+ GIT_SHALLOW TRUE
50
+ )
51
+ FetchContent_MakeAvailable(cjson)
52
+
53
+ add_executable(feature_extractor
54
+ src/main.cpp
55
+ src/extraction_action.cpp
56
+ src/macro_callbacks.cpp
57
+ src/json_writer.cpp
58
+ )
59
+
60
+ target_include_directories(feature_extractor PRIVATE include)
61
+ target_include_directories(feature_extractor SYSTEM PRIVATE
62
+ ${LLVM_INCLUDE_DIRS}
63
+ ${CLANG_INCLUDE_DIRS}
64
+ ${cjson_SOURCE_DIR}
65
+ )
66
+ target_compile_definitions(feature_extractor PRIVATE ${LLVM_DEFINITIONS})
67
+ target_compile_definitions(feature_extractor PRIVATE
68
+ CLANG_RESOURCE_DIR="${CLANG_RESOURCE_DIR}"
69
+ CLANG_SYSROOT="${CLANG_SYSROOT}"
70
+ )
71
+
72
+ if(NOT LLVM_ENABLE_RTTI)
73
+ target_compile_options(feature_extractor PRIVATE -fno-rtti)
74
+ endif()
75
+
76
+ target_compile_options(feature_extractor PRIVATE
77
+ -Wall -Wextra -Werror -Wpedantic -Wshadow
78
+ )
79
+
80
+ target_link_libraries(feature_extractor PRIVATE
81
+ clangTooling
82
+ clangASTMatchers
83
+ clangBasic
84
+ clangFrontend
85
+ clangAST
86
+ clangLex
87
+ cjson
88
+ )
89
+
90
+ install(TARGETS feature_extractor RUNTIME DESTINATION bin)
@@ -0,0 +1,136 @@
1
+ #pragma once
2
+
3
+ #include <clang/Basic/LangOptions.h>
4
+ #include <clang/Basic/SourceManager.h>
5
+ #include <clang/Frontend/FrontendAction.h>
6
+ #include <clang/Lex/PPCallbacks.h>
7
+ #include <clang/Tooling/Tooling.h>
8
+ #include <llvm/ADT/StringRef.h>
9
+
10
+ #include <memory>
11
+ #include <optional>
12
+ #include <string>
13
+ #include <unordered_set>
14
+ #include <vector>
15
+
16
+ namespace feature_extractor {
17
+
18
+ struct Param {
19
+ std::string name;
20
+ std::string type;
21
+ };
22
+
23
+ struct FunctionInfo {
24
+ std::string name;
25
+ std::string return_type;
26
+ std::vector<Param> params;
27
+ std::string signature;
28
+ bool is_public_api = false;
29
+ std::string header_path;
30
+ };
31
+
32
+ struct TypedefInfo {
33
+ std::string name;
34
+ std::string underlying_type;
35
+ std::string header_path;
36
+ };
37
+
38
+ struct MacroInfo {
39
+ std::string name;
40
+ bool is_function_like = false;
41
+ std::vector<std::string> params;
42
+ std::string value;
43
+ std::string header_path;
44
+ };
45
+
46
+ struct Enumerator {
47
+ std::string name;
48
+ long long value = 0;
49
+ };
50
+
51
+ struct EnumInfo {
52
+ std::optional<std::string> name;
53
+ std::vector<Enumerator> enumerators;
54
+ std::string header_path;
55
+ };
56
+
57
+ struct Field {
58
+ std::string name;
59
+ std::string type;
60
+ };
61
+
62
+ struct RecordInfo {
63
+ std::optional<std::string> name;
64
+ std::string kind; // "struct" | "union"
65
+ std::vector<Field> fields;
66
+ std::string header_path;
67
+ };
68
+
69
+ struct FeatureArtifact {
70
+ int schema_version = 1;
71
+ std::string project_name;
72
+ std::string language; // "c" | "c++" — must match fuzzprep.library_builder.models.Language
73
+ std::vector<FunctionInfo> functions;
74
+ std::vector<TypedefInfo> typedefs;
75
+ std::vector<MacroInfo> macros;
76
+ std::vector<EnumInfo> enums;
77
+ std::vector<RecordInfo> records;
78
+ std::vector<std::string> warnings;
79
+ };
80
+
81
+ // The project source root: the common ancestor of every translation unit's file
82
+ // path, which for an out-of-tree build is not where compile_commands.json lives.
83
+ // header_path values are relative to it, and a declaration inside it counts as
84
+ // library-owned rather than third-party or system.
85
+ struct ProjectContext {
86
+ std::string project_root;
87
+ };
88
+
89
+ // Not thread-safe. ClangTool runs one translation unit at a time, so one
90
+ // collector can be shared across every TU to accumulate and deduplicate
91
+ // declarations that appear in more than one (via a shared header, say).
92
+ class FeatureCollector {
93
+ public:
94
+ void addFunction(FunctionInfo info);
95
+ void addTypedef(TypedefInfo info);
96
+ void addMacro(MacroInfo info);
97
+ void addEnum(EnumInfo info);
98
+ void addRecord(RecordInfo info);
99
+ void addWarning(std::string warning);
100
+
101
+ const FeatureArtifact &artifact() const { return artifact_; }
102
+
103
+ private:
104
+ FeatureArtifact artifact_;
105
+ std::unordered_set<std::string> seen_functions_;
106
+ std::unordered_set<std::string> seen_typedefs_;
107
+ std::unordered_set<std::string> seen_macros_;
108
+ std::unordered_set<std::string> seen_enums_;
109
+ std::unordered_set<std::string> seen_records_;
110
+ };
111
+
112
+ // True when abs_path is inside ctx.project_root, i.e. library-owned rather than a
113
+ // system or third-party header.
114
+ bool isWithinProject(const ProjectContext &ctx, llvm::StringRef abs_path);
115
+
116
+ // abs_path relative to ctx.project_root when it is within the project, and
117
+ // unchanged otherwise.
118
+ std::string relativeHeaderPath(const ProjectContext &ctx,
119
+ llvm::StringRef abs_path);
120
+
121
+ // Builds the ASTFrontendAction that extracts functions, typedefs, enums, and
122
+ // records from one translation unit, and registers the macro-extracting
123
+ // PPCallbacks alongside it. Both write into collector.
124
+ std::unique_ptr<clang::tooling::FrontendActionFactory>
125
+ newExtractionActionFactory(FeatureCollector &collector, ProjectContext ctx);
126
+
127
+ // Registers a PPCallbacks that extracts macro definitions into collector.
128
+ std::unique_ptr<clang::PPCallbacks>
129
+ newMacroCollectorCallbacks(FeatureCollector &collector, ProjectContext ctx,
130
+ const clang::SourceManager &sm,
131
+ const clang::LangOptions &lang_opts);
132
+
133
+ // Serializes artifact to the JSON shape features.json readers expect.
134
+ std::string writeJson(const FeatureArtifact &artifact);
135
+
136
+ } // namespace feature_extractor