feat: add C/C++ language detection and dependency resolution with Makefile generation

Add comprehensive C and C++ project analysis including header classification (stdlib, POSIX, external), compiler flag suggestions, and Makefile generation. Extend DependencyResolver with C library package mappings for debian, fedora, arch, and brew platforms. Update ProjectAnalyzer with LANGUAGE_EXTENSIONS and BUILD_FILES mappings, rename python_version to language_version, and add build_system and compiler_flags fields to AnalysisResult. Enhance SafeCommandExecutor with incomplete argument detection for find, grep, and sed commands. Add metadata field to OperationResult in TransactionalFileSystem and fix hidden directory validation logic. Bump version to 1.69.0 and promote development status to Production/Stable.
This commit is contained in:
2025-12-13 05:30:08 +00:00
parent fc1d0e2ff5
commit 35ddb1ea6a
8 changed files with 1115 additions and 225 deletions
+301 -1
View File
@@ -1,3 +1,5 @@
# retoor <retoor@molodetz.nl>
import pytest
from rp.core.project_analyzer import ProjectAnalyzer, AnalysisResult
@@ -22,6 +24,7 @@ class User(BaseModel):
)
assert isinstance(result, AnalysisResult)
assert result.language == 'python'
assert 'requests' in result.dependencies
assert 'pydantic' in result.dependencies
@@ -78,7 +81,7 @@ if (x := 10) > 5:
code_content=code_with_walrus,
)
version_parts = result.python_version.split('.')
version_parts = result.language_version.split('.')
assert int(version_parts[1]) >= 8
def test_directory_structure_planning(self):
@@ -154,3 +157,300 @@ from fastapi.middleware.gzip import GZIPMiddleware
assert not result.valid
assert any('GZIPMiddleware' in str(e) or 'fastapi' in str(e).lower() for e in result.errors)
class TestCLanguageAnalyzer:
def setup_method(self):
self.analyzer = ProjectAnalyzer()
def test_detect_c_language(self):
c_code = """
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
printf("Hello, World!\\n");
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert result.language == 'c'
def test_c_standard_headers_detection(self):
c_code = """
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
int main() {
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert 'stdio.h' in result.dependencies
assert result.dependencies['stdio.h'] == 'stdlib'
assert result.dependencies['math.h'] == 'stdlib'
def test_c_posix_headers_detection(self):
c_code = """
#include <unistd.h>
#include <pthread.h>
#include <sys/socket.h>
int main() {
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert 'unistd.h' in result.dependencies
assert result.dependencies['unistd.h'] == 'posix'
assert any('POSIX' in w for w in result.warnings)
def test_c_local_headers_detection(self):
c_code = """
#include <stdio.h>
#include "myheader.h"
#include "utils/helper.h"
int main() {
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert 'myheader.h' in result.dependencies
assert result.dependencies['myheader.h'] == 'local'
def test_c_external_library_headers(self):
c_code = """
#include <stdio.h>
#include <curl/curl.h>
#include <openssl/ssl.h>
int main() {
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert 'curl/curl.h' in result.dependencies
assert result.dependencies['curl/curl.h'] == 'curl'
assert any('curl' in w for w in result.warnings)
def test_c_standard_detection_c99(self):
c_code = """
#include <stdio.h>
int main() {
for (int i = 0; i < 10; i++) {
printf("%d\\n", i);
}
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert result.language_version == 'c99'
def test_c_standard_detection_c11(self):
c_code = """
#include <stdio.h>
#include <stdatomic.h>
int main() {
_Static_assert(sizeof(int) >= 4, "int must be at least 4 bytes");
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert result.language_version == 'c11'
def test_c_standard_detection_gnu(self):
c_code = """
#include <stdio.h>
int main() {
typeof(5) x = 10;
printf("%d\\n", x);
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert 'gnu' in result.language_version
def test_c_compiler_flags_suggestion(self):
c_code = """
#include <stdio.h>
#include <math.h>
#include <pthread.h>
int main() {
pthread_t thread;
double x = sqrt(2.0);
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert '-lm' in result.compiler_flags
assert '-pthread' in result.compiler_flags
assert any('-std=' in f for f in result.compiler_flags)
assert '-Wall' in result.compiler_flags
def test_c_valid_analysis_no_errors(self):
c_code = """
#include <stdio.h>
int main() {
printf("Hello\\n");
return 0;
}
"""
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
)
assert result.valid
assert len(result.errors) == 0
def test_c_shell_commands_validation(self):
c_code = """
#include <stdio.h>
int main() { return 0; }
"""
commands = [
"gcc -o main main.c",
"make clean",
"./main",
]
result = self.analyzer.analyze_requirements(
spec_file="main.c",
code_content=c_code,
commands=commands,
)
valid_commands = [c for c in result.shell_commands if c['valid']]
assert len(valid_commands) == 3
class TestLanguageDetection:
def setup_method(self):
self.analyzer = ProjectAnalyzer()
def test_detect_python_from_content(self):
python_code = """
def hello():
print("Hello, World!")
if __name__ == "__main__":
hello()
"""
lang = self.analyzer.detect_language(python_code)
assert lang == 'python'
def test_detect_c_from_content(self):
c_code = """
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello\\n");
return 0;
}
"""
lang = self.analyzer.detect_language(c_code)
assert lang == 'c'
def test_detect_cpp_from_content(self):
cpp_code = """
#include <iostream>
int main() {
std::cout << "Hello" << std::endl;
return 0;
}
"""
lang = self.analyzer.detect_language(cpp_code)
assert lang == 'cpp'
def test_detect_rust_from_content(self):
rust_code = """
fn main() {
let x = 5;
println!("x = {}", x);
}
"""
lang = self.analyzer.detect_language(rust_code)
assert lang == 'rust'
def test_detect_go_from_content(self):
go_code = """
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
"""
lang = self.analyzer.detect_language(go_code)
assert lang == 'go'
def test_detect_javascript_from_content(self):
js_code = """
const express = require('express');
function hello() {
console.log("Hello");
}
export default hello;
"""
lang = self.analyzer.detect_language(js_code)
assert lang == 'javascript'
def test_detect_language_from_file_extension(self):
lang = self.analyzer.detect_language("", "main.c")
assert lang == 'c'
lang = self.analyzer.detect_language("", "app.py")
assert lang == 'python'
def test_detect_unknown_language(self):
weird_content = "some random text without any language patterns"
lang = self.analyzer.detect_language(weird_content)
assert lang == 'unknown'