시작하기
이 가이드는 SafeC 설치, 첫 프로그램 컴파일, 기본적인 컴파일 워크플로 이해까지 안내합니다.
설치
설치 스크립트를 실행합니다. 이 스크립트는 GitHub Releases에서 미리 빌드된 safec/safeguard/sc-lsp 릴리스를 다운로드하고 셸 환경을 구성합니다 — 빌드할 것은 없습니다. 다만 머신에 clang 21 이상이 있어야 합니다(SafeC는 LLVM IR을 clang에 넘겨 컴파일·링크하고, clang이 C 헤더를 가져옵니다). 플랫폼별 설치 방법은 설치에 있습니다.
curl -fsSL https://raw.githubusercontent.com/safec-org/SafeC/main/install.sh | bash
irm https://raw.githubusercontent.com/safec-org/SafeC/main/install.ps1 | iex
설치 스크립트 옵션
| 옵션 | 설명 |
|---|---|
--prefix=<path> |
설치 디렉터리 (기본값: ~/safec) |
--version=<tag> |
설치할 릴리스 태그, 예: v1.0.0-beta-7 (기본값: 프리릴리스를 포함한 최신 릴리스) |
--std=<sections> |
설치할 미리 빌드된 std 섹션, 예: core,collections,gui (기본값: all). std 소스는 항상 설치되며, 설치되지 않은 섹션은 빌드에 필요할 때 소스에서 컴파일됩니다 |
--std=+<sections> |
기존 설치본에 미리 빌드된 std 섹션을 추가합니다. 설치한 릴리스에서 가져옵니다 |
--skip-env |
셸 환경 구성 건너뛰기 |
install.ps1은 같은 옵션을 -Prefix, -Version, -Std, -SkipEnv로 받습니다.
배포되는 바이너리는 macOS arm64, Linux x86_64, Windows x86_64입니다. 전체 에셋 목록은 Releases를 참고하십시오. 요구 사항은 다음과 같습니다.
- macOS 11(Big Sur) 이상: 바이너리와 미리 빌드된 std는 11.0용으로 빌드됩니다.
- glibc 2.25 이상의 Linux(Debian 10, Ubuntu 18.04, RHEL 8 이후): 바이너리는 더 오래된 glibc에 대해 빌드되고 이를 검사하며, 미리 빌드된 std는 2.25보다 새로운 함수(
getrandom이 2.25)를 호출하지 않습니다. musl 기반 배포판(Alpine)은 릴리스 바이너리에서 지원하지 않습니다.
TIP
소스에서 빌드하는 것(릴리스 워크플로가 배포하지 않는 플랫폼을 위해 필요하거나, 릴리스되지 않은 커밋에서 빌드하기 위해 필요)도 여전히 가능합니다. compiler/, safeguard/, 그리고 자매 저장소인 sc-language-server의 각 구성 요소 자체 CMakeLists.txt를 통해서입니다 — 설치 → 소스에서와 컴파일러 아키텍처를 참고하십시오.
설치를 확인합니다:
safec --version # safec 1.0.0-beta-7
safeguard --version # safeguard 1.0.0-beta-7
첫 프로그램
safeguard로 새 프로젝트를 만듭니다:
safeguard new hello
cd hello
이렇게 하면 다음과 같은 구조가 생성됩니다:
hello/
.gitignore
Package.toml
src/
main.sc
src/main.sc를 다음으로 교체합니다:
extern int printf(const char* fmt, ...);
int main() {
printf("Hello from SafeC!\n");
return 0;
}
플레이그라운드에서 실행빌드하고 실행합니다:
safeguard build
safeguard run
출력:
Hello from SafeC!
더 완전한 예제
이 예제는 리전, 참조, 배열, 슬라이스를 보여줍니다:
extern int printf(const char* fmt, ...);
int main() {
// 명시적 리전 참조를 가진 스택 변수
int x = 42;
&stack int ref = &x;
printf("x = %d\n", *ref);
// 경계 검사가 이루어지는 배열
int arr[5];
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
// 슬라이스 — 배열에 대한 경계 검사가 이루어진 뷰
[]int s = arr[0..3];
printf("slice length = %ld\n", s.len);
return 0;
}
플레이그라운드에서 실행컴파일 파이프라인
SafeC 컴파일러는 소스 파일을 다단계 파이프라인을 통해 처리합니다:
Source (.sc)
|
v
Preprocessor #define, #include, #ifdef, -D/-I 플래그
|
v
Lexer 토큰화
|
v
Parser 재귀 하강 파싱 -> AST
|
v
Sema 타입 검사, 리전 분석, 대여 검사
|
v
ConstEval 컴파일 타임 함수 평가, static_assert
|
v
CodeGen LLVM IR 생성
|
v
LLVM IR (.ll) 텍스트 IR 또는 비트코드 출력
|
v
clang/lld 네이티브 바이너리로 링크
컴파일러 플래그
safec <input.sc> [input2.sc ...] [options]
safec가 받는 플래그는 아래가 전부입니다(safec --help는 SafeC에는 필요 없는 LLVM 자체의 옵션도 함께 나열합니다).
출력
| 플래그 | 설명 |
|---|---|
-o <file> |
출력 파일 경로(기본값 -, 표준 출력). 입력이 여러 개이고 --emit-bin이 없으면 입력마다 출력 하나가 생기는 디렉터리, 입력이 여러 개이고 --emit-bin이면 링크된 바이너리 하나 |
--emit-llvm |
LLVM IR을 텍스트(.ll)로 출력. 없으면 LLVM 비트코드를 출력 |
--emit-bin |
시스템 clang을 거쳐 네이티브 바이너리나 라이브러리를 -o로 바로 컴파일·링크(링킹 참고) |
-j <N> |
입력 파일이 여러 개일 때의 병렬 컴파일 작업 수(기본값: 하드웨어 스레드 수) |
최적화와 링킹
| 플래그 | 설명 |
|---|---|
-O <level> |
최적화 수준: 0(기본값), 1, 2, 3, s, z |
--release |
릴리스 프로파일: -O가 없으면 -O2 |
-l <name> |
라이브러리 name과 링크(--emit-bin 전용) |
-L <dir> |
-l용 라이브러리 검색 디렉터리 추가(--emit-bin 전용) |
--shared |
--emit-bin과 함께, 실행 파일 대신 공유 라이브러리를 생성 |
--static-lib |
--emit-bin과 함께, 정적 라이브러리(.a, ar 사용)를 생성 |
--lto[=thin|full] |
--emit-bin과 함께, 링크 타임 최적화(값이 없으면 thin) |
타깃
| 플래그 | 설명 |
|---|---|
--target=<alias|triple> |
교차 컴파일: 베어메탈 별칭(arm-7em, rv32i, wasm32, ...) 또는 임의의 LLVM 트리플(기본값: 호스트) |
--target-cpu=<name> |
타깃 CPU(기본값: 트리플의 범용 CPU) |
--target-features=<+a,-b> |
타깃 기능, LLVM -mattr 문법 |
--target-abi=<name> |
타깃 ABI(ilp32e, lp64e, ...) |
--freestanding |
프리스탠딩 모드: 호스트 런타임도 C 헤더 가져오기도 없음(베어메탈 별칭은 이를 함축) |
--provides=<fn,...> |
다른 번역 단위가 정의하는 함수(예: 베어메탈 런타임의 malloc). 이들을 호출해도 프리스탠딩 경고가 나지 않음 |
디버그 및 진단
| 플래그 | 설명 |
|---|---|
--dump-ast |
AST를 출력하고 종료 |
--dump-pp |
전처리된 소스를 출력하고 종료 |
--g lines |
라인 테이블 디버그 정보(DWARF) 출력 |
--g full |
지역 변수를 포함한 전체 디버그 정보 출력 |
-v |
상세 출력 |
컴파일 제어
| 플래그 | 설명 |
|---|---|
--no-sema |
의미 분석 건너뛰기 |
--no-consteval |
컴파일 타임 평가 단계 건너뛰기 |
--compat-preprocessor |
완전한 C 전처리기 호환성 활성화(함수형 매크로, 토큰 붙이기, 문자열화 — 전처리기 참고) |
--no-import-c-headers |
자동 C 헤더 가져오기 비활성화 |
--no-strict-aliasing |
타입 기반 별칭 정보를 내보내지 않음. 포인터 캐스트로 타입 퍼닝하는 unsafe 코드용 (엄격한 별칭 규칙 참고) |
전처리기
| 플래그 | 설명 |
|---|---|
-I <dir> |
include 검색 경로 추가 |
-D NAME[=VALUE] |
전처리기 매크로 정의 |
증분 컴파일
증분 컴파일(파일 단위 .bc 캐싱)은 기본적으로 켜져 있습니다 — 활성화를 위한 별도 플래그가 필요하지 않습니다.
| 플래그 | 설명 |
|---|---|
--no-incremental |
증분 컴파일 비활성화 (항상 처음부터 재컴파일) |
--cache-dir <dir> |
캐시 디렉터리 설정 (기본값: .safec_cache) |
--clear-cache |
캐시된 모든 .bc 파일을 지우고 종료 |
문서
safeguard doc이 패키지의 선언을 읽을 때 실행하는 것입니다.
| 플래그 | 설명 |
|---|---|
--emit-doc-json |
입력을 파싱하고(의미 분석과 코드 생성 없음) 선언과 문서 주석을 JSON으로 출력 |
--doc-root <dir> |
dir 아래의 파일만 문서화(반복 가능). 출력의 경로는 이에 대한 상대 경로 |
--doc-extern <name>=<dir> |
dir 아래의 선언을 외부 패키지 name의 항목으로 나열(그 레퍼런스로의 링크용) |
--doc-private |
static 함수와 전역 변수도 포함 |
정보
이들은 입력 파일이 필요 없습니다.
| 플래그 | 출력 |
|---|---|
--version |
컴파일러 버전 |
--list-targets |
베어메탈 타깃 별칭과 그 트리플, ABI |
--print-target-clang-flags --target=<t> |
그 타깃의 IR을 컴파일·링크하는 clang 플래그 |
--print-clang |
safec가 IR을 넘기는 clang(SAFEC_CLANG이 이를 덮어씀) |
C 헤더 임포트
SafeC는 C 표준 헤더를 네이티브로 포함할 수 있습니다. 컴파일러는 C 헤더 선언을 파싱하여 수동 extern 스텁 없이 사용할 수 있게 합니다:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
?&char msg = malloc(64); // raw void* -> ?&char: implicit
match (msg) {
case some(m): {
strcpy(m, "Hello from C headers!");
printf("%s\n", m);
}
case null: { }
}
free(msg);
return 0;
}
플레이그라운드에서 실행자동 임포트는 포함된 헤더에 대해 clang -ast-dump=json을 호출하고 함수 및 typedef 선언을 추출하는 방식으로 동작합니다. 수동 extern 선언을 선호한다면 --no-import-c-headers로 비활성화합니다.
패키지 매니저 사용하기
의존성이 있거나 여러 소스 파일이 있는 프로젝트라면 safeguard를 사용합니다. 설치 스크립트가 safec, sc-lsp와 함께 설치하므로 이미 사용할 수 있습니다.
# 새 프로젝트 생성
safeguard new myproject
cd myproject
# 빌드 및 실행
safeguard build
safeguard run
safeguard new가 생성하는 프로젝트 구조:
myproject/
.gitignore
Package.toml # 프로젝트 매니페스트
src/
main.sc # 진입점
설치 스크립트는 이미 SAFEC_HOME(설치 prefix, 기본값 ~/safec)을 설정하고 이를 PATH에 추가하여 safeguard가 컴파일러와 표준 라이브러리를 자동으로 찾을 수 있도록 합니다 — 설치 후 셸을 다시 시작하거나 셸 rc 파일을 source합니다. 이 단계를 건너뛰었거나(--skip-env) 설치 위치를 옮겼다면 수동으로 설정합니다:
export SAFEC_HOME=/path/to/safec
export PATH="$SAFEC_HOME/bin:$PATH"
링킹
SafeC는 LLVM IR로 컴파일됩니다. safec --emit-bin -o hello는 clang을 거쳐 한 번에 링크하고(링킹), safeguard는 프로젝트를 대신 링크합니다. 직접 할 때는 최종 링킹 단계에 시스템의 C 툴체인을 사용합니다:
# 기본 링킹
clang hello.ll -o hello
# pthreads 사용 (spawn/join용)
clang concurrency.ll -lpthread -o concurrency
# 수학 라이브러리 사용
clang math_demo.ll -lm -o math_demo
에디터 지원
.sc 파일을 위한 LSP 서버가 제공되며, 다음을 지원합니다:
- 진단 (오류 강조 표시)
- 호버 정보
- 코드 완성
- 정의로 이동
- 문서 심볼
서버는 설치 스크립트가 PATH에 넣는 sc-lsp입니다. VS Code, Neovim, Emacs 설정은 도구에 있습니다(VS Code 확장은 sc-language-server 저장소의 editors/vscode/ 디렉터리에 있습니다).