


관리형 래퍼와 네이티브 라이브러리를 ABI를 깨지 않고 고치기
네이티브 C 라이브러리를 감싸는 크로스 플랫폼 관리형 래퍼를 고칠 때 사용합니다. 모든 계층에서 변경을 따라가고, 오래된 다운로드 대신 소스에서 네이티브 바이너리를 빌드하며, 공개 ABI를 안정적으로 지키고, 포인터 소유권과 스레드를 바르게 다루며, 실패하는 테스트를 건너뛰지 않습니다.
이 AI 규칙을 사용하려면
## 관리형 래퍼에서 네이티브 코드까지 변경을 따라가기
라이브러리를 관리형 래퍼, 상호 운용 선언, 안정적인 C 호환 API, 네이티브 구현으로 이루어진 계층형 시스템으로 다룬다.
- 공개 입력은 관리형 래퍼에서 검증한다.
- C 호환 브리지는 작고 예측 가능하게 유지한다. 정책을 중복 구현하지 말고 변환과 전달만 담당하게 한다.
- 관리형 선언, 내보낸 C 심볼, 매개변수 형식, 반환 형식, 네이티브 구현이 정확히 일치하도록 유지한다.
- 코드를 편집하기 전에 각 검증 규칙, 메모리 할당, 오류 변환, 수명 전환을 어느 계층이 담당하는지 결정한다.
- 첫 번째 컴파일 오류만 수정하지 말고 영향을 받는 모든 계층에서 변경을 추적한다.
관리형 계층에서 컴파일되는 변경도 네이티브 바이너리에 새 심볼이 없거나 다른 시그니처를 사용하면 런타임에 실패할 수 있다.
## 변경 유형에 따라 미리 빌드된 바이너리와 소스 빌드 바이너리 중 고르기
관리형 래퍼를 빌드하기 전에 네이티브 바이너리가 있어야 하지만, 올바른 바이너리 출처는 변경 내용에 따라 달라진다.
- 관리형 코드만 변경할 때는 현재 출시된 마일스톤에 대해 저장소가 승인한 사전 빌드 네이티브 결과물을 사용한다.
- 네이티브 구현, C API, 종속성, 하위 모듈, 버전, 마일스톤을 변경할 때는 대상 플랫폼과 아키텍처용 네이티브 바이너리를 소스에서 빌드한다.
- 네이티브 측에 영향을 주는 변경 후에는 해당 변경이 결과물 제공처에 출시될 때까지 현재의 사전 빌드 바이너리를 다운로드해 사용하는 것을 금지한다.
- 네이티브 소스 빌드가 실패하면 도구 체인, 종속성, 경로, 네트워크 전제 조건을 진단한다. 관리형 빌드를 통과시키려는 목적으로 오래된 다운로드 결과물로 절대 대체하지 않는다.
- 플랫폼 캐시에 이전 네이티브 모듈이 남아 잘못된 테스트 결과를 만들 수 있으면 캐시를 정리한다.
API 변경 후 오래된 네이티브 바이너리를 사용하면 겉으로 드러나지 않는 잘못된 빌드가 생성되고, 이후 런타임에 진입점 누락 오류가 발생할 수 있다.
## 바인딩은 직접 고치지 않고 다시 생성하며 ABI를 안정적으로 지키기
생성된 바인딩, 생성 문서, 렌더링된 릴리스 페이지 또는 생성 결과로 지정된 다른 파일을 수동으로 편집하지 않는다. 편집 가능한 소스를 변경하고 C API 변경 후에는 저장소의 생성기를 실행한다.
애플리케이션 바이너리 인터페이스를 안정적으로 유지한다.
- 기존 시그니처를 변경하지 말고 오버로드, 메서드, 형식을 추가한다.
- 공개 API를 제거하거나 반환 형식을 변경하지 않는다.
- 기본 매개변수는 바이너리 호환성을 바꿀 수 있으므로 새 기본 매개변수보다 오버로드를 우선한다.
- API를 제거하지 말고 사용 중단 처리하며, 실행 가능한 마이그레이션 지침을 제공한다.
- 명명 및 매개변수 규칙을 주변 공개 API와 일관되게 유지한다.
- 빌드 전에 바인딩을 다시 생성하고 생성된 차이를 확인한다.
문서에 생성 페이지와 편집 가능한 보조 소스가 함께 있다면 지정된 소스만 편집하고 결과를 다시 렌더링한다.
## 네이티브 포인터 소유권을 나누고 변경 가능한 객체를 스레드 간에 공유하지 않기
래퍼를 작성하기 전에 모든 네이티브 포인터를 분류한다.
- 빌린 매개변수는 소유권을 이전하지 않는다.
- 수동 소유 네이티브 객체에는 래퍼가 정한 네이티브 해제 경로가 필요하다.
- 참조 횟수 기반 객체는 래퍼의 참조 횟수 계약을 사용해야 한다.
실패 동작을 해당 계층에 맞춘다. C 호환 API는 null 포인터 또는 `false`를 반환하고, 관리형 팩터리는 `null`을 반환하며, 유효한 객체를 만들 수 없는 관리형 생성자는 예외를 발생시킨다.
일부 네이티브 작업은 전달받은 것과 동일한 인스턴스를 반환할 수 있다. 원본을 해제하기 전에 객체 동일성을 비교한다. 그렇지 않으면 반환값이 이미 해제된 객체를 참조할 수 있다.
네이티브 계약에서 명시적으로 허용하지 않는 한 변경 가능한 네이티브 객체를 스레드 간에 공유하지 않는다. 각 스레드에 독립적인 변경 가능 상태를 제공하고, 변경 불가능하며 스레드 안전하다고 문서화된 객체만 공유한다.
고정 범위가 끝난 뒤에도 네이티브 코드가 유지할 수 있는 임시 고정 관리형 포인터를 절대 전달하지 않는다. 수명이 명시된 고정 핸들을 사용하거나 비관리 메모리를 할당한다. 이 규칙을 위반하면 네이티브 코드가 이전 주소를 계속 보유하는 동안 가비지 컬렉터가 메모리를 이동할 수 있다.
## 상위 저장소와 네이티브 서브모듈 모두에서 기능 브랜치 쓰기
커밋하기 전에 기능 브랜치를 만든다. 보호된 브랜치에 직접 커밋하지 않는다.
네이티브 하위 모듈이나 중첩 저장소에도 같은 규칙을 독립적으로 적용한다. 각각 자체 브랜치 상태, 보호 브랜치, 커밋, 검토 경로를 가진다.
1. 상위 저장소에 범위가 명확한 기능 브랜치를 만든다.
2. 중첩 네이티브 저장소를 변경한다면 편집하거나 커밋하기 전에 그 저장소에 기능 브랜치를 만든다.
3. 모든 커밋을 해당 기능 브랜치에 유지한다.
4. 저장소가 승인한 pull request 작업 흐름을 통해 변경 사항을 검토에 제출한다.
5. 상위 저장소가 의도한 검토 완료 네이티브 리비전을 기록하는지 확인한다.
저장소가 지정한 shim 또는 확장 영역 밖에 있는 상위 프로젝트 관리 네이티브 디렉터리를 변경하지 않는다.
## 올바른 네이티브 바이너리로 테스트하고 실패를 건너뛰지 않기
빌드 성공만으로 완료된 것은 아니다. 올바른 부트스트랩 경로에서 생성한 네이티브 바이너리를 대상으로 관련 관리형 테스트를 실행한다.
- 완료를 주장하기 전에 테스트가 통과해야 한다.
- 회귀를 우회하기 위해 실패한 테스트를 건너뛰거나 건너뛰기 예외를 도입하지 않는다.
- 화면 없음, 그래픽 장치 사용 불가, 플랫폼의 기능 미지원처럼 입증된 하드웨어 또는 플랫폼 제약이 있을 때만 건너뛰기를 허용한다.
- C API가 변경되었다면 테스트 전에 바인딩을 다시 생성한다.
- 버전 변경을 검증하기 전에 캐시된 네이티브 웹 또는 플랫폼 결과물을 정리한다.
- 네이티브 측에 영향을 주는 작업은 대표적인 플랫폼과 아키텍처에서 검증한다.
소유권, null 처리, 내보낸 심볼, ABI, 스레드 범위 또는 해제 동작이 잘못되었을 때 실패하는 테스트를 작성한다. 정확한 동작이나 이점이 아직 입증되지 않은 성능 변경에는 동등성 테스트와 벤치마크를 포함한다.
## 실패 모양을 보고 계층 간 문제 위치 찾기
정상으로 확인된 기준 상태를 확립하고, 한 번에 변수 하나만 변경하며, 각 결과를 기록하고 플랫폼 차이를 증거로 다룬다. 변경으로 상태가 나빠지면 다른 가설을 시험하기 전에 해당 변경을 되돌린다.
오류 형태를 사용해 먼저 조사할 경계를 선택한다.
- 관리형 형식 누락은 바인딩을 다시 생성하지 않았거나 포함하지 않았음을 시사한다.
- 네이티브 심볼을 확인할 수 없으면 C 선언과 구현이 일치하지 않을 가능성이 있다.
- 런타임 진입점 누락은 오래된 네이티브 바이너리 또는 내보내기 불일치를 강하게 시사한다.
- 접근 위반은 소유권, 해제, 고정 또는 시그니처 손상을 시사한다.
- 팩터리 호출 후 null 참조 오류는 네이티브 계층이 실패를 반환했지만 호출자가 이를 무시했음을 시사한다.
- 불규칙한 스레드 간 비정상 종료는 변경 가능한 네이티브 객체가 공유되었음을 시사한다.
완료 전에 생성된 차이를 확인하고, 필요할 때 네이티브 코드를 빌드하고, 관리형 래퍼를 빌드하고, 테스트를 실행하고, 영향을 받는 플랫폼 매트릭스를 검증한다. 사용한 부트스트랩 출처와 아키텍처를 정확히 보고한다. 테스트가 실패하거나, 필요한 네이티브 빌드를 다운로드 결과물로 대체했거나, 플랫폼 검증이 완료되지 않았다면 성공을 주장하지 않는다.
