149 lines
15 KiB
Markdown
149 lines
15 KiB
Markdown
# 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` 실행에서 실패했다.
|
|
|
|
```powershell
|
|
cmake --build .harness/build --config Debug
|
|
```
|
|
|
|
오류는 C++ 컴파일 전에 `ZERO_CHECK`의 `GetOutOfDateItems` 작업에서 발생했다.
|
|
|
|
```text
|
|
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 접근 규칙과 정확한 내부 경로가 거부됐는지까지 확정한 것은 아니다.
|
|
|
|
## 앞선 조치와 구분해야 할 문제
|
|
|
|
1. 최초 configure의 `FESA_GTEST_SOURCE_DIR` 누락은 사용자 지정 `C:/git/googletest`와 설치된 MKL/TBB/HDF5 경로로 해결했다.
|
|
2. 긴 TEMP 경로에서 기존 pressure 테스트의 결과 잠금 파일 생성이 실패했다. 동일 binary의 TEMP 경로만 바꾼 비교에서 긴 경로는 실패하고 짧은 경로는 통과했다. 이후 TEMP/TMP는 새 `.harness/build/t-<8hex>`, pytest basetemp는 새 `.harness/build/p-<8hex>`를 사용하고 각각 80자 이하를 확인한다.
|
|
3. 짧은 TEMP/TMP를 build와 같은 native shell에 직접 설정해도 FileTracker 접근 오류가 재발했다. 따라서 TEMP 대입 누락이나 경로 길이 문제만으로 이 빌드 오류를 설명하지 않는다.
|
|
|
|
## 적용한 Ninja 구성
|
|
|
|
- 기존 `windows-msvc-ninja-debug` configure/build/test preset을 함께 사용한다.
|
|
- `.harness/config.example.json`의 설정을 ignored `.harness/config.json`으로 사용한다. 예상 밖 기존 설정은 덮어쓰지 않는다.
|
|
- 새 binary directory는 `.harness/build-ninja-msvc-debug`다. 기존 VS `.harness/build` cache와 실행 기록을 보존한다.
|
|
- 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](implementation-environment.md#e8-attempt3-후의-정상-backend-대안)에 있다.
|
|
|
|
아래는 `E8`에서 사용한 핵심 검증 명령이다. 후속 재검증은 아직 실행하지 않은 [E9 계획](implementation-environment.md#e9-attempt4-후의-직렬-ninja-검증)의 병렬도 설정과 진입 조건을 함께 따른다.
|
|
|
|
```powershell
|
|
$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](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은 계속 별도로 보존한다.
|
|
|
|
검토할 최소 변경은 다음 한 값이다. **아직 적용하거나 승인받지 않았다.**
|
|
|
|
```text
|
|
대상: 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 문서](https://learn.chatgpt.com/docs/hooks)
|
|
|
|
조회 근거는 [기본 설정 해석](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-hook-config-resolution.json), [정상 root marker 지정 조회](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-hook-root-marker-observation.json), [두 파일의 읽기 전용 감사](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-hook-source-audit.json)에 있다. Hook 실행·canonical 설정 변경·trust 상태 변경은 수행하지 않았다.
|
|
|
|
## 근거 기록
|
|
|
|
- [동일 native build shell의 TEMP/TMP 설정과 FileTracker 실패](../../.harness/build/mitc4-eigen-evidence/prep/attempt3/root-manual-build-environment-observation.json)
|
|
- [새 Ninja 환경 준비 결과](../../.harness/build/mitc4-eigen-evidence/prep/attempt3/e8-root-setup/result.json), [실제 선택된 compiler·dependency·archive 경로](../../.harness/build/mitc4-eigen-evidence/prep/attempt3/e8-root-setup/configuration-observation.json)
|
|
- [최초 Ninja manual build 원시 로그](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/manual-cmake-d2d41bcf6bb242f4b484ea02850d762e/build.log): UTF-16LE, SHA-256 `47484e659a73977626c549fb56783ad5416ff14260e48e559b37b6b93f358f96`
|
|
- [설치된 CMake 모듈과 생성된 discovery 경로의 읽기 전용 감사](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-gtest-discovery-path-observation.json): SHA-256 `9d383f04a347df95b742403ceca4732b331ddf4ec96437993f0ceffaa060bf59`
|
|
- [실제 프로세스 종료 관찰](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-process-observations.json): CMake PID `1916`, CTest `21912`, Python `3492`/`24968`, 외부 wrapper `23560`, Harness 부모 `21996`을 command·parent와 함께 대조했다. 파일의 `role`은 탐색용 분류다. `rg` 소스 조회 shell인 PID `10528`은 Hook 실행에서 제외한다.
|
|
|
|
이 원시 로그는 해당 workspace의 ignored 검증 artifact다. 장기 workflow 상태와 후속 실행 판정은 [coordination.md](coordination.md)를 기준으로 한다.
|
|
|
|
## E10: 자동 Stop의 의존성 경로 누락 해결
|
|
|
|
초기 `E8` 설정은 process 단위였다. 설명문서 커밋 뒤 별도 자동 Stop에서는 부모 환경의 의존성 변수 네 개가 없었고, 공통 preset의 `$env{...}`가 cache를 빈 값으로 덮어써 `FESA_GTEST_SOURCE_DIR` 오류가 재발했다. [당시 cache와 관찰 기록](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-stop-after-decision-docs/observation.json)을 보존했다.
|
|
|
|
이를 해결하기 위해 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 환경 문서](implementation-environment.md#e10-부모-환경과-독립적인-workspace-user-preset)에 있다.
|
|
|
|
```powershell
|
|
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 성공을 전체 구현 준비 통과로 대체하지 않음 |
|
|
|
|
근거는 [최초 실행 결과](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-e10-setup/result.json), [후속 configure 결과](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-e10-setup/configure-followup/result.json), [최종 설정·명령 감사](../../.harness/build/mitc4-eigen-evidence/prep/attempt4/root-e10-setup/configuration-observation.json)에 있다. 기존 solver library hash, source, phase history와 원본 checkout의 Hook은 보존했다.
|
|
|
|
로컬 preset·설정·전용 TEMP는 workspace에 유지한다. 설치 경로 변경이나 feature 종료 때 설정 수명을 검토한다. 이 조치는 위 공유 Hook의 1800초 제한과 재신뢰 승인 문제를 해결한 것으로 처리하지 않는다.
|