15 KiB
MSVC 빌드 접근 오류와 Ninja 전환
- 작성 목적: MITC4 질량행렬·FEAST 구현 준비 중 발생한 Windows 빌드 환경 오류와 조치 기록
- Feature ID:
mitc4-shell-feast-eigen - Owner:
Root(사용자 요청 설명문서) - 입력 계약:
r2,E8실행 결과,E9미실행 검증 계획과E10로컬 설정 결과 - 작성일: 2026-09-06
- 상태: MSVC 컴파일·링크 및 후속 CTest 통과 확인. 최초 GoogleTest discovery 오류와 wrapper 비정상 종료로 전체 준비 검증 미완료
MSVC 컴파일러와 x64/Debug 조건을 유지하면서 CMake의 빌드 generator를 Visual Studio에서 프로젝트가 이미 지원하는 Ninja로 바꿨다. 기본 workspace-write 권한에서 MSVC 컴파일·링크까지 진행했고, 이후 별도 GoogleTest discovery 문제를 확인했다. 최초 manual 빌드가 실패했으므로 전체 준비 검증은 아직 완료되지 않았다.
관찰된 오류
Visual Studio generator의 다음 명령이 native workspace-write 실행에서 실패했다.
cmake --build .harness/build --config Debug
오류는 C++ 컴파일 전에 ZERO_CHECK의 GetOutOfDateItems 작업에서 발생했다.
MSB4018: GetOutOfDateItems
System.TypeInitializationException: Microsoft.Build.Utilities.FileTracker
System.UnauthorizedAccessException: 0x80070005 (E_ACCESSDENIED)
Microsoft.Build.Framework.NativeMethods.GetLongFilePath
Microsoft.Build.Utilities.FileTracker..cctor()
같은 실행에서 configure는 성공했지만 manual Debug build는 exit 1이었다. 별도로 등록된 Stop의 build와 CTest는 성공했다. 따라서 자동 검증의 성공으로 native manual build 실패를 통과 처리하지 않았다.
확인된 범위는 MSBuild의 FileTracker 초기화에서 발생하는 접근 오류다. 어떤 Windows 접근 규칙과 정확한 내부 경로가 거부됐는지까지 확정한 것은 아니다.
앞선 조치와 구분해야 할 문제
- 최초 configure의
FESA_GTEST_SOURCE_DIR누락은 사용자 지정C:/git/googletest와 설치된 MKL/TBB/HDF5 경로로 해결했다. - 긴 TEMP 경로에서 기존 pressure 테스트의 결과 잠금 파일 생성이 실패했다. 동일 binary의 TEMP 경로만 바꾼 비교에서 긴 경로는 실패하고 짧은 경로는 통과했다. 이후 TEMP/TMP는 새
.harness/build/t-<8hex>, pytest basetemp는 새.harness/build/p-<8hex>를 사용하고 각각 80자 이하를 확인한다. - 짧은 TEMP/TMP를 build와 같은 native shell에 직접 설정해도 FileTracker 접근 오류가 재발했다. 따라서 TEMP 대입 누락이나 경로 길이 문제만으로 이 빌드 오류를 설명하지 않는다.
적용한 Ninja 구성
- 기존
windows-msvc-ninja-debugconfigure/build/test preset을 함께 사용한다. .harness/config.example.json의 설정을 ignored.harness/config.json으로 사용한다. 예상 밖 기존 설정은 덮어쓰지 않는다.- 새 binary directory는
.harness/build-ninja-msvc-debug다. 기존 VS.harness/buildcache와 실행 기록을 보존한다. - Harness를 시작하는 부모 shell에서 설치된
Launch-VsDevShell.ps1로 MSVC host/target x64 환경을 설정하고 Ninja 및 승인된 dependency 경로를 적용한다. 이번 실행의 자식 tool shell에서는 해당 스크립트 호출이 PowerShell 실행 정책으로 거부됐다. 실행 정책을 우회하지 않고 부모에서 상속된VSINSTALLDIR,VSCMD_ARG_TGT_ARCH=x64,INCLUDE,LIB,cl.exe,ninja.exe를 확인한 뒤 컴파일·링크를 진행했다. - 기존 reference helper가 실제 library의 수정 시각을 확인할 수 있도록
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG를 새 build의src/fesa/Debug로 지정한다. Library를 복사하거나 수정 시각을 조작하지 않는다. - 환경 변수는 해당 process에서만 설정하고
finally에서 복원한다. 정확한 설정·복원과 실행 순서는 구현 환경 문서 E8에 있다.
아래는 E8에서 사용한 핵심 검증 명령이다. 후속 재검증은 아직 실행하지 않은 E9 계획의 병렬도 설정과 진입 조건을 함께 따른다.
$mfeArchive = (Join-Path (Get-Location).Path '.harness/build-ninja-msvc-debug/src/fesa/Debug').Replace('\', '/')
cmake --preset windows-msvc-ninja-debug "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG=$mfeArchive"
cmake --build --preset windows-msvc-ninja-debug
ctest --preset windows-msvc-ninja-debug --show-only=json-v1
ctest --preset windows-msvc-ninja-debug --output-on-failure
실제 실행에서는 각 명령의 종료 코드를 즉시 확인하고 실패 시 다음 검증으로 진행하지 않는다. Registered Stop도 같은 preset과 binary directory를 사용한다. Test selection, assertion, source, 수치 계약, reference 원본과 검증 기준은 이 환경 전환에서 변경하지 않는다.
검증 결과
| 확인 항목 | 결과 |
|---|---|
| 짧은 TEMP를 직접 적용한 기존 native MSBuild | configure exit 0, build exit 1 (E_ACCESSDENIED) |
| 위 실행의 별도 registered Stop/CTest | exit 0, 362/362 통과 |
| Root의 새 Ninja 준비 configure | exit 0, 7.446초; MSVC Hostx64/x64, Debug, Ninja 및 dependency 경로 확인 |
| Root configure 전후 보존 확인 | tracked 변경 0, 기존 VS cache 동일, Git clean |
| 새 Ninja native Debug build | 컴파일 경고 0개; 컴파일·링크 이후 integration GoogleTest discovery의 JSON parsing 오류로 exit 1 |
| 새 Ninja manual CTest/full pytest | 최초 manual build 실패 뒤 미실행 |
| 새 Ninja registered 검증의 CMake/CTest | build exit 0, CTest exit 0, 362/362 통과 |
| 위 registered 검증을 감싼 프로세스 | Python 두 프로세스 exit 0, 외부 PowerShell wrapper exit 1; 전체 Hook 성공으로 합치지 않음 |
| E8.2 Harness invocation | native PowerShell exit 1; manual build 실패를 보존 |
Root의 configure 성공이나 후속 CTest 통과는 최초 manual build 실패를 대체하지 않는다. 이 결과만으로 전체 준비 단계의 통과를 주장하지 않는다. 세부 구현 담당 보고서는 implementation-prep-evidence.md를 참조한다.
Ninja에서 추가로 발견된 검증 장애
새 Ninja 빌드에서는 기존 FileTracker 접근 오류 대신, 컴파일·링크 후 CMake의 GoogleTest 목록 생성에서 ParseTestList.cmake:96 JSON parsing 오류가 발생했다. 현재 설치된 모듈 조합을 읽어 다음을 확인했다.
GoogleTestAddTests.cmake는TEST_TARGET의 SHA-256 앞 10자를 JSON 파일명으로 사용한다.GoogleTest.cmake와 실제 생성된 세 discovery 스크립트에는TEST_TARGET인수가 없다.- 세 스크립트가 같은 디렉터리의
cmake_test_discovery_e3b0c44298.json을 삭제·생성·읽는 구조다. - 동일 integration binary의 단독 목록 생성은 34개 테스트를 반환했고, 생성된 discovery 스크립트도 단독 실행에서는 통과했다.
이 관찰은 병렬 POST_BUILD discovery의 공유 파일 경쟁을 뒷받침한다. 정확한 최초 경쟁 순서를 재현한 것은 아니며, 단독 진단의 성공으로 최초 전체 빌드 실패를 통과 처리하지 않았다. 후속 E9 계획은 CMAKE_BUILD_PARALLEL_LEVEL=1과 한 번의 --clean-first --verbose 빌드로 세 discovery의 실제 직렬 실행을 검증한다. 설치된 모듈·생성된 JSON·테스트 소스와 discovery guard는 그대로이며, 이 후속 계획은 아직 미실행이다.
자동 검증의 별도 제한 시간 문제와 승인 대상
설치된 Codex CLI 0.153.2의 hooks/list·config/read를 모델 실행 없이 조회했다. 현재 worktree의 project config는 읽지만, 실제 enabled/trusted Stop Hook은 원본 checkout의 C:\git\FESADev\.codex\hooks.json에서 1800초로 선택됐다. 현재 worktree 파일의 7500초는 실제 Hook 선택에 반영되지 않았다. 공식 지원 project_root_markers를 해당 조회에만 지정해도 결과는 같았다.
이번 native raw에는 Hook 설정 출처 이벤트가 없으므로, 현재 resolver 결과와 당시 외부 PowerShell wrapper가 약 1802초 뒤 exit 1인 관찰을 대조한 진단이다. Python/CTest의 후속 exit 0과 wrapper exit 1은 계속 별도로 보존한다.
검토할 최소 변경은 다음 한 값이다. 아직 적용하거나 승인받지 않았다.
대상: C:\git\FESADev\.codex\hooks.json
필드: hooks.Stop[0].hooks[0].timeout
변경: 1800 -> 7500 (초)
- 현재 파일 SHA-256:
0ab23dad4643f45238f4a60402083e32d8801f5c41fd6ba12b68ef6a9fc5fc28 - 나머지 bytes를 보존하고 한 번 치환한 제안 파일 SHA-256:
c8065e7b60882da5db3f90d57b265da25b3c25980bf669d0b1da863e621871ca - 영향: 이 원본 Hook을 선택하는 다른 worktree에도 제한 시간 변경이 적용된다. Hook 명령, 검증 범위, assertion과 sandbox 권한은 그대로다.
- 승인 경계: 현재 feature worktree 밖의 공유 설정이다. 기존 단일
E6실행 승인을 이 파일 수정 권한으로 확대하지 않는다. 변경된 Hook 정의의 재신뢰도 별도로 처리해야 한다.
공식 문서는 여러 Hook source를 함께 로드하며 상위 layer가 기존 Hook을 대체하지 않는다고 설명한다. 또한 정의가 바뀌면 새 hash에 대한 신뢰 검토가 필요하다. 따라서 inline Hook 추가나 trust 상태의 임의 수정으로 해결하지 않는다. 공식 Hook 문서
조회 근거는 기본 설정 해석, 정상 root marker 지정 조회, 두 파일의 읽기 전용 감사에 있다. Hook 실행·canonical 설정 변경·trust 상태 변경은 수행하지 않았다.
근거 기록
- 동일 native build shell의 TEMP/TMP 설정과 FileTracker 실패
- 새 Ninja 환경 준비 결과, 실제 선택된 compiler·dependency·archive 경로
- 최초 Ninja manual build 원시 로그: UTF-16LE, SHA-256
47484e659a73977626c549fb56783ad5416ff14260e48e559b37b6b93f358f96 - 설치된 CMake 모듈과 생성된 discovery 경로의 읽기 전용 감사: SHA-256
9d383f04a347df95b742403ceca4732b331ddf4ec96437993f0ceffaa060bf59 - 실제 프로세스 종료 관찰: CMake PID
1916, CTest21912, Python3492/24968, 외부 wrapper23560, Harness 부모21996을 command·parent와 함께 대조했다. 파일의role은 탐색용 분류다.rg소스 조회 shell인 PID10528은 Hook 실행에서 제외한다.
이 원시 로그는 해당 workspace의 ignored 검증 artifact다. 장기 workflow 상태와 후속 실행 판정은 coordination.md를 기준으로 한다.
E10: 자동 Stop의 의존성 경로 누락 해결
초기 E8 설정은 process 단위였다. 설명문서 커밋 뒤 별도 자동 Stop에서는 부모 환경의 의존성 변수 네 개가 없었고, 공통 preset의 $env{...}가 cache를 빈 값으로 덮어써 FESA_GTEST_SOURCE_DIR 오류가 재발했다. 당시 cache와 관찰 기록을 보존했다.
이를 해결하기 위해 workspace 전용 CMakeUserPresets.json에 mfe-workspace-msvc-ninja-debug를 만들었다.
- 공통 preset을 상속하면서 GoogleTest
C:/git/googletest와 MKL/TBB/HDF5 설치 경로를 cache 값으로 직접 지정한다. - 정상 MSVC x64 개발 환경에서 얻은 INCLUDE/LIB 및 cl/rc/mt/Ninja 실행 경로를 담고, build/test가 configure 환경을 상속하도록 한다.
- CMake child의 TEMP/TMP는 설정 수명 동안 전용 70자 경로를 사용하고 빌드 병렬도는
1로 지정한다. Python/pytest의 새 basetemp 규칙은 유지한다. - ignored
.harness/config.json의 configure/build/test preset을 모두 같은 로컬 이름으로 맞춘다./CMakeUserPresets.json한 줄을.gitignore에 추가해3cfbefb로 커밋했다.
이제 해당 workspace에서는 부모 환경에 의존성 변수 네 개가 없어도 다음 명령이 설정을 읽는다. 전체 명령 대응과 복원 절차는 E10 환경 문서에 있다.
cmake --preset mfe-workspace-msvc-ninja-debug
| 확인 항목 | 실제 결과 |
|---|---|
| 새 preset 목록·local config 해석 | 통과 |
| 최초 E10 configure | exit 1. 의존성은 찾았지만 compiler 경로의 Hostx64/HostX64 표기 차이로 CMake가 cache를 재초기화했고, 내부 재configure에서 의존성 값이 사라짐 |
| compiler 문자열 일치 확인 뒤 동일 preset의 configure 한 번 | exit 0, 1.656초, stderr 0 bytes; 부모의 의존성 변수 네 개는 미설정 |
| 후속 cache·adapter 대조 | 의존성 네 경로, MSVC 19.51/x64, pointer 8 bytes, Ninja/Debug, Debug archive 경로 및 네 검증 명령의 local preset 일치 확인 |
| E10의 production build/CTest | 미실행; configure 성공을 전체 구현 준비 통과로 대체하지 않음 |
근거는 최초 실행 결과, 후속 configure 결과, 최종 설정·명령 감사에 있다. 기존 solver library hash, source, phase history와 원본 checkout의 Hook은 보존했다.
로컬 preset·설정·전용 TEMP는 workspace에 유지한다. 설치 경로 변경이나 feature 종료 때 설정 수명을 검토한다. 이 조치는 위 공유 Hook의 1800초 제한과 재신뢰 승인 문제를 해결한 것으로 처리하지 않는다.