引き続き、マルチ言語で祝日対応カレンダーを作っていこう。
前回については、以下を参照していただきたい。
なお、今回は、前回のスクリプトをすべて実行していることを前提としている。
スケジュール
第1日目:C言語、Go言語、Rustセットアップ(前回)
第2日目:Kotlin、Git & Pushセットアップ(今回)
今回は第2日目のご紹介となる。
なお、作業を自動化するスクリプトをご用意した。
スクリプト③(Kotlin完全インストール)
#!/bin/bash
# ========================================
# Multi-PG-Lang Calendar
# スクリプト③:Kotlin完全インストール
# SDKMANの問題を回避して確実にインストール
# ========================================
echo "🚀 Multi-PG-Lang Calendar - Part 3: Kotlin Complete Setup"
echo "==========================================================="
echo ""
# ========================================
# Step 1: プロジェクトルート検出
# ========================================
echo "📍 Step 1: プロジェクトルート検出"
echo "-----------------------------------"
if [ -d "/workspaces/multi-pg-lang-calendar" ]; then
PROJECT_ROOT="/workspaces/multi-pg-lang-calendar"
elif [ -d "$HOME/multi-pg-lang-calendar" ]; then
PROJECT_ROOT="$HOME/multi-pg-lang-calendar"
else
echo "❌ プロジェクトディレクトリが見つかりません"
exit 1
fi
cd "$PROJECT_ROOT"
echo "✅ プロジェクトルート: $(pwd)"
echo ""
# ========================================
# Step 2: Kotlinディレクトリ作成
# ========================================
echo "📁 Step 2: Kotlinディレクトリ作成"
echo "-----------------------------------"
mkdir -p kotlin
echo "✅ ディレクトリ作成完了"
echo ""
# ========================================
# Step 3: SDKMAN状態確認
# ========================================
echo "🔍 Step 3: SDKMAN状態確認"
echo "-----------------------------------"
# システムワイドのSDKMANを確認
if [ -d "/usr/local/sdkman" ]; then
echo "⚠️ システムワイドのSDKMANが検出されました: /usr/local/sdkman"
echo " このSDKMANは使用せず、ユーザーディレクトリにインストールします"
SDKMAN_SYSTEM=1
fi
# ユーザーディレクトリのSDKMAN
if [ -d "$HOME/.sdkman" ]; then
echo "✅ ユーザーSDKMAN検出: $HOME/.sdkman"
SDKMAN_USER=1
else
SDKMAN_USER=0
fi
echo ""
# ========================================
# Step 4: ユーザーSDKMANインストール
# ========================================
echo "🔧 Step 4: ユーザーSDKMANセットアップ"
echo "-----------------------------------"
if [ "$SDKMAN_USER" -eq 0 ]; then
echo "Installing SDKMAN to user directory..."
# ユーザーディレクトリにインストール
export SDKMAN_DIR="$HOME/.sdkman"
curl -s "https://get.sdkman.io" | bash
if [ $? -eq 0 ]; then
echo "✅ SDKMAN installed to $HOME/.sdkman"
else
echo "❌ SDKMAN installation failed"
exit 1
fi
else
echo "✅ SDKMAN already installed"
fi
# 環境変数設定
export SDKMAN_DIR="$HOME/.sdkman"
# sdkman-init.shを読み込み
if [ -f "$HOME/.sdkman/bin/sdkman-init.sh" ]; then
source "$HOME/.sdkman/bin/sdkman-init.sh"
echo "✅ SDKMAN environment loaded"
else
echo "❌ sdkman-init.sh not found"
exit 1
fi
echo ""
# ========================================
# Step 5: .bashrc / .zshrc に追記
# ========================================
echo "📝 Step 5: シェル設定ファイルに追記"
echo "-----------------------------------"
# .bashrc に追記
if [ -f "$HOME/.bashrc" ]; then
if ! grep -q "SDKMAN_DIR.*/.sdkman" "$HOME/.bashrc"; then
echo "" >> "$HOME/.bashrc"
echo "# SDKMAN Configuration" >> "$HOME/.bashrc"
echo "export SDKMAN_DIR=\"\$HOME/.sdkman\"" >> "$HOME/.bashrc"
echo "[[ -s \"\$HOME/.sdkman/bin/sdkman-init.sh\" ]] && source \"\$HOME/.sdkman/bin/sdkman-init.sh\"" >> "$HOME/.bashrc"
echo "✅ Added to ~/.bashrc"
else
echo "✅ Already in ~/.bashrc"
fi
fi
# .zshrc に追記(存在する場合)
if [ -f "$HOME/.zshrc" ]; then
if ! grep -q "SDKMAN_DIR.*/.sdkman" "$HOME/.zshrc"; then
echo "" >> "$HOME/.zshrc"
echo "# SDKMAN Configuration" >> "$HOME/.zshrc"
echo "export SDKMAN_DIR=\"\$HOME/.sdkman\"" >> "$HOME/.zshrc"
echo "[[ -s \"\$HOME/.sdkman/bin/sdkman-init.sh\" ]] && source \"\$HOME/.sdkman/bin/sdkman-init.sh\"" >> "$HOME/.zshrc"
echo "✅ Added to ~/.zshrc"
else
echo "✅ Already in ~/.zshrc"
fi
fi
echo ""
# ========================================
# Step 6: Kotlinインストール
# ========================================
echo "📦 Step 6: Kotlinインストール"
echo "-----------------------------------"
# 環境変数を再読み込み
export SDKMAN_DIR="$HOME/.sdkman"
source "$HOME/.sdkman/bin/sdkman-init.sh"
# Kotlinがインストールされているか確認
if command -v kotlin &> /dev/null; then
echo "✅ Kotlin already installed"
kotlin -version 2>&1 | head -1
else
echo "Installing Kotlin..."
# 明示的にユーザーSDKMANを使用してインストール
bash -c "export SDKMAN_DIR=$HOME/.sdkman && source $HOME/.sdkman/bin/sdkman-init.sh && sdk install kotlin" < /dev/null
# 環境変数を再読み込み
source "$HOME/.sdkman/bin/sdkman-init.sh"
# 確認
if command -v kotlin &> /dev/null; then
echo "✅ Kotlin installed successfully"
kotlin -version 2>&1 | head -1
else
echo "❌ Kotlin installation failed"
echo ""
echo "手動インストールを試してください:"
echo " source ~/.bashrc"
echo " sdk install kotlin"
exit 1
fi
fi
echo ""
# ========================================
# Step 7: Kotlinソースコード作成
# ========================================
echo "📝 Step 7: Kotlinソースコード作成"
echo "-----------------------------------"
cat > kotlin/calendar.kt << 'KOTLIN_SOURCE_EOF'
import java.io.File
import java.net.URL
import java.time.LocalDate
data class Holiday(
val year: Int,
val month: Int,
val day: Int,
val name: String
)
val weekdays = arrayOf("日", "月", "火", "水", "木", "金", "土")
val holidays = mutableListOf<Holiday>()
const val HOLIDAY_URL = "https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv"
const val HOLIDAY_FILE = "holidays.csv"
fun downloadAndConvertHolidayFile(): Boolean {
return try {
println("内閣府から祝日データをダウンロード中...")
val content = URL(HOLIDAY_URL).readBytes()
val text = try {
String(content, charset("Shift_JIS"))
} catch (e: Exception) {
String(content, Charsets.UTF_8)
}
File(HOLIDAY_FILE).writeText(text, Charsets.UTF_8)
println("祝日データを保存しました: $HOLIDAY_FILE")
true
} catch (e: Exception) {
println("ダウンロードエラー: ${e.message}")
false
}
}
fun loadHolidaysFromFile(filename: String): Boolean {
val file = File(filename)
if (!file.exists()) {
return false
}
try {
val lines = file.readLines(Charsets.UTF_8)
var lineNum = 0
for (line in lines) {
lineNum++
if (lineNum == 1) continue
val trimmedLine = line.trim()
if (trimmedLine.isEmpty()) continue
val parts = trimmedLine.split(",")
if (parts.size < 2) continue
val dateStr = parts[0].trim().replace("/", "-")
val name = parts[1].trim()
try {
val dateParts = dateStr.split("-")
if (dateParts.size != 3) continue
val year = dateParts[0].toInt()
val month = dateParts[1].toInt()
val day = dateParts[2].toInt()
holidays.add(Holiday(year, month, day, name))
} catch (e: NumberFormatException) {
// Skip invalid lines
}
}
return true
} catch (e: Exception) {
println("ファイル読み込みエラー: ${e.message}")
return false
}
}
fun isHoliday(year: Int, month: Int, day: Int): Pair<Boolean, String> {
val holiday = holidays.find { it.year == year && it.month == month && it.day == day }
return if (holiday != null) {
Pair(true, holiday.name)
} else {
Pair(false, "")
}
}
fun printCalendar(year: Int, month: Int) {
println("\n ${year}年 ${month}月")
println("----------------------------")
weekdays.forEach { print(" $it ") }
println()
println("----------------------------")
val firstDay = LocalDate.of(year, month, 1)
val firstWeekday = firstDay.dayOfWeek.value % 7
val daysInMonth = firstDay.lengthOfMonth()
repeat(firstWeekday) {
print(" ")
}
var currentWeekday = firstWeekday
for (day in 1..daysInMonth) {
val (isHol, _) = isHoliday(year, month, day)
if (isHol) {
print("%3d*".format(day))
} else {
print("%3d ".format(day))
}
currentWeekday++
if (currentWeekday == 7) {
println()
currentWeekday = 0
}
}
if (currentWeekday != 0) {
println()
}
println("----------------------------")
println("\n【祝日】")
val monthHolidays = holidays.filter { it.year == year && it.month == month }
if (monthHolidays.isEmpty()) {
println(" なし")
} else {
monthHolidays.sortedBy { it.day }.forEach {
println(" %2d日: %s".format(it.day, it.name))
}
}
println()
}
fun main() {
println("=== 月間カレンダー(祝日対応版)Kotlin ===\n")
if (!loadHolidaysFromFile(HOLIDAY_FILE)) {
println("ローカルファイル '$HOLIDAY_FILE' が見つかりません。")
print("内閣府から祝日データをダウンロードしますか? (y/n): ")
val response = readLine()?.trim()?.lowercase()
if (response == "y") {
if (downloadAndConvertHolidayFile()) {
if (loadHolidaysFromFile(HOLIDAY_FILE)) {
println("祝日データを読み込みました: ${holidays.size}件")
} else {
println("読み込みエラーが発生しました")
}
} else {
println("祝日データなしで続行します。")
}
} else {
println("祝日データなしで続行します。")
}
} else {
println("祝日データを読み込みました: ${holidays.size}件")
}
print("\n年を入力してください (例: 2025): ")
val year = readLine()?.toIntOrNull() ?: 2025
print("月を入力してください (1-12): ")
val month = readLine()?.toIntOrNull() ?: 1
if (month !in 1..12) {
println("月は1から12の間で入力してください。")
return
}
printCalendar(year, month)
}
KOTLIN_SOURCE_EOF
echo "✅ kotlin/calendar.kt 作成完了"
echo ""
# ========================================
# Step 8: シンボリックリンク作成
# ========================================
echo "🔗 Step 8: シンボリックリンク作成"
echo "-----------------------------------"
cd kotlin
ln -sf ../data/holidays.csv holidays.csv
echo "✅ kotlin/holidays.csv -> ../data/holidays.csv"
cd "$PROJECT_ROOT"
echo ""
# ========================================
# Step 9: Kotlinビルド
# ========================================
echo "🔨 Step 9: Kotlinビルド"
echo "-----------------------------------"
cd kotlin
# 環境変数を再読み込み
export SDKMAN_DIR="$HOME/.sdkman"
source "$HOME/.sdkman/bin/sdkman-init.sh"
echo "Kotlinをビルド中..."
echo ""
if kotlinc calendar.kt -include-runtime -d calendar.jar 2>&1 | tee /tmp/kotlin_build.log; then
if [ -f "calendar.jar" ]; then
echo ""
echo "✅ Kotlinビルド成功"
ls -lh calendar.jar
BUILD_SUCCESS=1
else
echo ""
echo "❌ Kotlinビルド失敗(JARファイルが作成されませんでした)"
BUILD_SUCCESS=0
fi
else
echo ""
echo "❌ Kotlinビルド失敗"
BUILD_SUCCESS=0
fi
cd "$PROJECT_ROOT"
echo ""
# ========================================
# Step 10: Kotlinテスト実行
# ========================================
if [ "$BUILD_SUCCESS" -eq 1 ]; then
echo "🧪 Step 10: Kotlinテスト実行 (2025年5月)"
echo "-----------------------------------"
echo ""
cd kotlin
echo -e "2025\n5" | java -jar calendar.jar
cd "$PROJECT_ROOT"
echo ""
fi
# ========================================
# Step 11: README更新
# ========================================
echo "📝 Step 11: README更新"
echo "-----------------------------------"
cat > README.md << 'README_EOF'
# 🗓️ Multi-PG-Lang Calendar
Calendar App implemented in C, Go, Kotlin & Rust
C言語、Go、Kotlin、Rustで実装した祝日対応カレンダー
## ✨ Features
- 🌐 4つのプログラミング言語で同じ機能を実装
- 📥 内閣府の祝日データ自動取得(UTF-8変換対応)
- 🚀 GitHub Codespaces で即座に試せる
- 📊 言語間のパフォーマンス比較が可能
## 🚀 Quick Start
### All Languages (自動ビルド&テスト)
```bash
./scripts/build-test-all.sh
```
### Individual Build & Test
#### C
```bash
cd c && make
echo -e "2025\n5" | ./calendar
```
#### Go
```bash
cd go
go build -o calendar calendar.go
echo -e "2025\n5" | ./calendar
```
#### Kotlin
```bash
cd kotlin
kotlinc calendar.kt -include-runtime -d calendar.jar
echo -e "2025\n5" | java -jar calendar.jar
```
#### Rust
```bash
cd rust
cargo build --release
echo -e "2025\n5" | ./target/release/calendar
```
## 📁 Directory Structure
```
multi-pg-lang-calendar/
├── c/ # C言語実装
├── go/ # Go実装
├── kotlin/ # Kotlin実装
├── rust/ # Rust実装
├── data/ # 共通データ(祝日CSV・UTF-8)
└── scripts/ # ビルド・テストスクリプト
```
## 📄 License
MIT License
README_EOF
echo "✅ README.md 更新完了"
echo ""
# ========================================
# Step 12: 統合テストスクリプト作成
# ========================================
echo "📝 Step 12: 統合テストスクリプト更新"
echo "-----------------------------------"
cat > scripts/build-test-all.sh << 'BUILD_ALL_EOF'
#!/bin/bash
GREEN='\033[0;32m'
RED='\033[0;31m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
cd "$PROJECT_ROOT"
echo -e "${YELLOW}🔨 Building All Languages...${NC}"
echo "======================================"
echo ""
# 環境変数読み込み
if [ -f "$HOME/.sdkman/bin/sdkman-init.sh" ]; then
export SDKMAN_DIR="$HOME/.sdkman"
source "$HOME/.sdkman/bin/sdkman-init.sh"
fi
if [ -f "$HOME/.cargo/env" ]; then
export PATH="$HOME/.cargo/bin:$PATH"
source "$HOME/.cargo/env"
fi
BUILD_SUCCESS=0
BUILD_FAILED=0
# C言語
echo -e "${BLUE}📦 Building C...${NC}"
cd "$PROJECT_ROOT/c"
if make clean > /dev/null 2>&1 && make; then
echo -e "${GREEN}✅ C build successful${NC}"
BUILD_SUCCESS=$((BUILD_SUCCESS + 1))
else
echo -e "${RED}❌ C build failed${NC}"
BUILD_FAILED=$((BUILD_FAILED + 1))
fi
echo ""
# Go
echo -e "${BLUE}📦 Building Go...${NC}"
cd "$PROJECT_ROOT/go"
if go build -o calendar calendar.go; then
echo -e "${GREEN}✅ Go build successful${NC}"
BUILD_SUCCESS=$((BUILD_SUCCESS + 1))
else
echo -e "${RED}❌ Go build failed${NC}"
BUILD_FAILED=$((BUILD_FAILED + 1))
fi
echo ""
# Rust
echo -e "${BLUE}📦 Building Rust...${NC}"
cd "$PROJECT_ROOT/rust"
if cargo build --release 2>&1 | tail -1 | grep -q "Finished"; then
echo -e "${GREEN}✅ Rust build successful${NC}"
BUILD_SUCCESS=$((BUILD_SUCCESS + 1))
else
echo -e "${RED}❌ Rust build failed${NC}"
BUILD_FAILED=$((BUILD_FAILED + 1))
fi
echo ""
# Kotlin
echo -e "${BLUE}📦 Building Kotlin...${NC}"
cd "$PROJECT_ROOT/kotlin"
if command -v kotlinc &> /dev/null; then
if kotlinc calendar.kt -include-runtime -d calendar.jar 2>/dev/null; then
echo -e "${GREEN}✅ Kotlin build successful${NC}"
BUILD_SUCCESS=$((BUILD_SUCCESS + 1))
else
echo -e "${RED}❌ Kotlin build failed${NC}"
BUILD_FAILED=$((BUILD_FAILED + 1))
fi
else
echo -e "${YELLOW}⚠️ kotlinc not found - skipping Kotlin build${NC}"
fi
echo ""
cd "$PROJECT_ROOT"
echo "======================================"
echo -e "${YELLOW}📊 Build Summary${NC}"
echo "======================================"
echo -e "${GREEN}Success: $BUILD_SUCCESS${NC}"
echo -e "${RED}Failed: $BUILD_FAILED${NC}"
echo ""
# テスト実行
echo "======================================"
echo -e "${YELLOW}🧪 Testing All Languages (2025年5月)${NC}"
echo "======================================"
echo ""
TEST_INPUT="2025
5"
# C
if [ -f "c/calendar" ]; then
echo -e "${BLUE}--- C言語 ---${NC}"
cd c
echo "$TEST_INPUT" | ./calendar | tail -20
cd ..
echo ""
fi
# Go
if [ -f "go/calendar" ]; then
echo -e "${BLUE}--- Go言語 ---${NC}"
cd go
echo "$TEST_INPUT" | ./calendar | tail -20
cd ..
echo ""
fi
# Rust
if [ -f "rust/target/release/calendar" ]; then
echo -e "${BLUE}--- Rust ---${NC}"
cd rust
echo "$TEST_INPUT" | ./target/release/calendar | tail -20
cd ..
echo ""
fi
# Kotlin
if [ -f "kotlin/calendar.jar" ]; then
echo -e "${BLUE}--- Kotlin ---${NC}"
cd kotlin
echo "$TEST_INPUT" | java -jar calendar.jar | tail -20
cd ..
echo ""
fi
echo "======================================"
echo -e "${GREEN}✅ All tests complete!${NC}"
echo "======================================"
BUILD_ALL_EOF
chmod +x scripts/build-test-all.sh
echo "✅ scripts/build-test-all.sh 更新完了"
echo ""
# ========================================
# 完了メッセージ
# ========================================
echo "=========================================="
echo "✨ Kotlinセットアップ完了!"
echo "=========================================="
echo ""
echo "📁 プロジェクト: $PROJECT_ROOT"
echo ""
if [ "$BUILD_SUCCESS" -eq 1 ]; then
echo "✅ Kotlin: ビルド成功"
echo ""
echo "📝 次のステップ:"
echo "-----------------------------------"
echo " 1. Kotlinを手動実行:"
echo " cd $PROJECT_ROOT/kotlin"
echo " echo -e '2025\n5' | java -jar calendar.jar"
echo ""
echo " 2. 全言語を一括テスト:"
echo " cd $PROJECT_ROOT"
echo " ./scripts/build-test-all.sh"
echo ""
echo " 3. 新しいターミナルで常にKotlinを使用可能にする:"
echo " source ~/.bashrc"
echo ""
else
echo "⚠️ Kotlin: ビルド失敗"
echo ""
echo "【トラブルシューティング】"
echo "-----------------------------------"
echo "1. 新しいターミナルを開く"
echo ""
echo "2. 環境変数を読み込む:"
echo " source ~/.bashrc"
echo ""
echo "3. Kotlinが使えるか確認:"
echo " kotlin -version"
echo ""
echo "4. ビルドを再試行:"
echo " cd $PROJECT_ROOT/kotlin"
echo " kotlinc calendar.kt -include-runtime -d calendar.jar"
echo ""
echo "5. 実行:"
echo " echo -e '2025\n5' | java -jar calendar.jar"
echo ""
fi
echo "=========================================="
echo ""実行手順
第2日目:Kotlinのセットアップ
以下は、GitHub Codespacesの起動後、ターミナルで実行しよう。
# プロジェクトディレクトリに移動
cd /workspaces/multi-pg-lang-calendar
# ========================================
# スクリプト③ Kotlin追加セットアップ
# ========================================
# 1. ファイルを作成
nano setup-kotlin.sh
# 2. スクリプト③の内容をコピー&ペースト
# 3. 保存して終了(Ctrl+O → Enter → Ctrl+X)
# 4. 実行権限を付与
chmod +x setup-kotlin.sh
# 5. 実行
./setup-kotlin.sh実行例
(略)
✅ Kotlinビルド成功
-rw-rw-rw- 1 codespace codespace 4.9M Nov 1 22:41 calendar.jar
🧪 Step 10: Kotlinテスト実行 (2025年5月)
-----------------------------------
=== 月間カレンダー(祝日対応版)Kotlin ===
祝日データを読み込みました: 1050件
年を入力してください (例: 2025): 月を入力してください (1-12):
2025年 5月
----------------------------
日 月 火 水 木 金 土
----------------------------
1 2 3*
4* 5* 6* 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
----------------------------
【祝日】
3日: 憲法記念日
4日: みどりの日
5日: こどもの日
6日: 休日
📝 Step 11: README更新
-----------------------------------
✅ README.md 更新完了
📝 Step 12: 統合テストスクリプト更新
-----------------------------------
✅ scripts/build-test-all.sh 更新完了
==========================================
✨ Kotlinセットアップ完了!
==========================================
(略)スクリプト③実行後のGit&Push手順
cd /workspaces/multi-pg-lang-calendar
# 1. サブディレクトリの.git削除
rm -rf rust/.git kotlin/.git c/.git go/.git
# 確認(プロジェクトルートの.gitのみ残っているはず)
find . -name ".git" -type d
# 2. Git初期化(未実施の場合)
git init
git branch -M main
# 3. ステージング
git add .
# 4. 状態確認(重要!)
git status
# 5. コミット
git commit -m "Initial commit: Multi-PG-Lang Calendar (C, Go, Kotlin, Rust)"
# 6. リモート設定
git remote add origin https://github.com/your-username/multi-pg-lang-calendar.git
# 7. 強制プッシュ
git push -u origin main --force
# 8. 最終確認
git status
git remote -vステップ解説
- サブディレクトリの.git削除 → エラー回避
- Git初期化 → リポジトリ作成
- ステージング → 変更を記録準備
- 状態確認 ← エラーがないか確認(重要!)
- コミット → 変更を記録
- リモート設定 → GitHub接続
- 強制プッシュ → アップロード
- 最終確認 → 成功確認
git statusでエラーメッセージが出ないことを確認してからコミットするのが安全である。
確認例
git status
On branch main
Your branch is up to date with 'origin/main'.
git remote -v
origin https://github.com/your-username/multi-pg-lang-calendar.git (fetch)
origin https://github.com/your-username/multi-pg-lang-calendar.git (push)


コメント