Files
FESADev/docs/mitc4-shell-feast-eigen/msvc-build-access-error.md
T
2026-09-06 17:40:39 +09:00

124 lines
12 KiB
Markdown

# MSVC 빌드 접근 오류와 Ninja 전환
- 작성 목적: MITC4 질량행렬·FEAST 구현 준비 중 발생한 Windows 빌드 환경 오류와 조치 기록
- Feature ID: `mitc4-shell-feast-eigen`
- Owner: `Root` (사용자 요청 설명문서)
- 입력 계약: `r2`, `E8` 실행 결과와 `E9` 미실행 환경 계획
- 작성일: 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)를 기준으로 한다.
## 설정 수명
이 환경은 process 단위다. Root의 준비 명령이 종료돼도 다른 shell에 dependency 환경이 지속된다고 가정할 수 없다. 이후 개발 shell에는 같은 MSVC/dependency 환경을 적용해야 하며, feature 종료 때 local 설정의 유지·원상복구와 Root outer Stop 환경을 별도로 확인한다.