Pkl(コンフィグレーション構成生成:静的型付言語)
アプリ/Web開発etc…で利用するJSON/YAMLやXMLで書かれたコンフィグファイル生成言語がAppleからOpenSourceで公開されました。
コンフィグファイル生成に便利なのでサンプルを作ってみました。
Apple、コンフィグレーション生成用の静的型付き言語「Pkl」をオープンソースで公開、単一コードからJSONやYAML、XMLなどを生成
マニュアル
手順
インストール(curlでローカルへコピー&動作テスト)
Pathが通っている場所へコピー
pklコマンドの実行
インストール
各プラットフォーム向けのインストール手順に添います。
まず、Console(Terminal)を使って自分のマシンタイプを調べます。
以下のコマンドをConsoleで実行します。(※①)
% uname -m
![](https://assets.st-note.com/img/1707112641502-8NhgUfN3Hu.png)
今回は、M1 Macなので、arm64系ということが判明しました。
マシンタイプはarm64系のMacOSなので、
% curl -L -o pkl https://github.com/apple/pkl/releases/download/0.25.1/pkl-macos-amd64
chmod +x pkl
./pkl --version
を実行します。
※ 任意の場所で実行すると、pklというファイル名で実行ファイルがダウンロードされていると思います。
正常にダウンロード(インストール)できていれば、
(※2)
$ ./pkl --version
が実行され、Pklのバージョンを表示します。
![](https://assets.st-note.com/img/1707118004319-vrbqCeSyUR.png)
コピー
正常にインストールが完了されたことを確認後、
pkl(実行ファイル)をシステム環境(PATH)に追加しておくといつでもConsole(Terminal)から利用できます。
brew環境の場合は、which brewでPATHを検索します。
# brewのPATHを検索
$ which brew
# 結果
/usr/local/bin/brew
pklを/usr/local/bin/に移動し、
同様にwhich pklを実行してみましよう。
$ mv ./pkl /usr/local/bin/pkl
# pklのPATHを検索(確認)
$ which pkl
# 結果: /usr/local/bin/pkl
brew PATHにpklが追加されました。
pklコマンドの実行
環境ができたので実行していきます。
$ pkl -h
$ pkl eval -f json [Pklファイル]
...
コマンドラインヘルプ
$ pkl -h
$ pkl --help
初めてのコンフィグファイル作成(intro.pkl)
任意の場所にintro.pklというファイルを作成します。
// intro.pkl
name = "Pkl: Configure your Systems in New Ways"
attendants = 100
isInteractive = true
amountLearned = 13.37
出力
作成したintro.pklをjson形式で出力します。
$ pkl -f json intro.pkl
実行結果:
// 出力結果
{
"name": "Pkl: Configure your Systems in New Ways",
"attendants": 100,
"isInteractive": true,
"amountLearned": 13.37
}
ファイル出力
作成したintro.pklをjsonファイルに書き出す場合は以下の通りにします。
$ pkl -f json intro.pkl -o intro.json
対応ファイルフォーマット
JSON/YAML/XML/plist …
Pythonでも利用できるようにしてみる
# File: pkl.py
# License: MIT License
# Date: 2024.02.17
# Written by oeOvOao
import os, sys, argparse, subprocess
class PklCLI:
# pkl実行ファイルまでのパスは環境に応じて適宜変更
pkl = "/usr/local/bin/pkl"
source=None
file_type=None
command_line=False
output_file=None
shell=[]
shell_option=[]
def __init__(self, source=None, type=None):
self.source = source
if type is None or type == '':
self.file_type = "json"
else:
self.file_type = type
def set_cli(self, command_line=True):
self.command_line = command_line
print('[Show Help]: $ python3 pkl.py -h')
def get_cli(self):
return self.command_line
def cli(self):
parser = argparse.ArgumentParser(
prog="PKL Python CLI(v.1.0)",
usage=f'%(prog)s [HELP] -h/--help',
)
parser.add_argument("-s", "-source", default='intro.pkl', help=f"-s/-source [Pkl File]", required=True)
parser.add_argument("--t", "--type", default='json', help=f"--t/--type [json, yaml, plist, xml]")
return parser.parse_args()
def output(self):
split_file = self.source.split("/")[-1]
file = ".".join(split_file.split(".")[:-1])
self.output_file = f"{file}.{self.file_type}"
self.shell_option = ["-o", self.output_file]
self.shell += self.shell_option
def print_file(self, view=False):
message = f'''[CLI]: {self.get_cli()}
[File Type(Output)]: {self.file_type}
[Source File]: {self.source}
[Output File]: {self.output_file}'''
if view:
print(message)
def run(self, output=False):
if self.get_cli():
args = self.cli()
# program
self.source = args.s
if os.path.isfile(self.source)==False:
print('Source File Not Found.')
exit()
self.file_type = args.t
self.shell = [self.pkl, "eval", "-f", self.file_type, self.source]
if self.source is None or (os.path.isfile(self.source) == False):
print("Source File Not found.")
exit()
print(f'[Preview]: {self.source}')
if output:
subprocess.run(self.shell)
self.output()
subprocess.run(self.shell)
return self.output_file
# main.py
from pkl import PklCLI
import os
# Example
# WWW ... etc
def main():
pkl=PklCLI(
'/Users/ryohei/Pictures/Drawthings/opencv/config/sample.pkl',
'json'
)
# コマンドラインで呼び出したい場合、
# pkl.set_cli()
# command line help: $ python3 pkl.py -h
# pklからコンフィグ生成(JSON/YAML...) : run(output=True)
config_file = pkl.run(output=False)
file_text = None
if config_file is None:
message = '''##########################################################################
PklCLI run method argument is not True
PklCLI (Example):
pkl = PklCLI('/pth/to/xxxx.pkl', 'json')
config_file = pkl.run(True)
or
config_file = PklCLI('/pth/to/xxxx.pkl', 'json').run(True)
##########################################################################
'''
print(message)
exit()
if os.path.isfile(config_file):
with open(config_file, 'r') as fp:
file_text = fp.read()
print(f'Config: {config_file}')
print(file_text)
if __name__ == '__main__':
main()
# 実行は、
$ python3 main.py
これでpythonからpklを扱ってJSON…etcの書き出しに対応できたので、FlaskみたいなWebApplicationとも連携できますね。
あとは、node.jsで変換できると良いのかな?
Node.jsでも利用してみる。
// pkl.js
/* *****************************************
Code: PKL2Node.js
License: MIT License
Written by oeOvOao
Date: 2024/02/17
$ node index.js [PKL File] [File Type(JSON/YAML/plist/XML)]
example:
const PKLCli = require('./pkl').PKL;
const pkl = new PKLCli('/path/to/xxxx.pkl', 'json');
pkl.run()
********************************************/
const exec = require('child_process').exec;
const fs = require('fs');
class PKLCli{
// pklコマンドは適宜変更ください。
#pkl = '/usr/local/bin/pkl';
constructor(file, type){
this.file = file;
this.type = type;
}
run(){
const argv = process.argv;
const pkl = this.#pkl
const pkl_file = (argv[2] != null) ? argv[2]: `${this.file}`;
if(!fs.existsSync(pkl_file)){
console.log(`File Not Found > ${pkl_file}`);
return 0
}
const file_type = (argv[3] != null) ? argv[3] : `${this.type}`
const file_name = `${process.env.PWD}/${pkl_file.split('/')[pkl_file.split('/').length-1].split('.')[0]}.${file_type}`
exec(`${pkl} eval -f ${file_type} -o ${file_name} ${pkl_file}`, (err, stdout, stderr) => {
if (err) { console.log(err); }
console.log(stdout);
});
console.log(`Edit FIle: ${pkl_file}`);
return file_name;
}
}
module.exports = {
PKL: PKLCli
}
// index.js
const fs = require('fs');
const PKLCli = require('./pkl').PKL
const main = ()=>{
const pkl = new PKLCli(`${process.env.PWD}/intro.pkl`, 'json')
const file_name = pkl.run()
if (fs.existsSync( file_name )){
const txt = fs.readFileSync(file_name, {encoding: 'utf-8'})
// const _json = JSON.parse(txt);
console.log(`Read File > ${file_name}`)
console.log(txt);
}
else{
console.log(`File Create > ${file_name}`)
}
}
main()
# 実行は、
$ node index.js
or
$ node index.js /path/to/xxxxx.pkl
Chrome 拡張機能のmanifest(v.3)を書いてみる
// manifest.pkl
name="Chrome Apps(v3) Package Sample"
version="1.0.0"
manifest_version=3
description="Your Chrome App Description"
icons=new {
`128`="icons/icon_app_128.png"
}
content_scripts=new {
new {
matches = new {
"*://*/*"
}
js = new {
"js/content.js"
}
}
}
action = new {
default_title = "Template Title"
default_popup = "popup/popup.html"
}
permissions=new {
"scripting"
"activeTab"
}
※ keyが数値・記号の場合、`(バッククォート)で数値・記号を囲みます。
# Preview
$ pkl eval -f json /path/to/manifest.pkl
// 生成結果(Preview)
{
"name": "Chrome Apps(v3) Package Sample",
"version": "1.0.0",
"manifest_version": 3,
"description": "Your Chrome App Description",
"icons": {
"128": "icons/icon_app_128.png"
},
"content_scripts": [
{
"matches": [
"*://*/*"
],
"js": [
"js/content.js"
]
}
],
"action": {
"default_title": "Template Title",
"default_popup": "popup/popup.html"
},
"permissions": [
"scripting",
"activeTab"
]
}
マニフェストファイル形式参考:
開発でいろいろな場面でJSON/YAMLを管理する場面が多いので、計算もできるPKLを使う場面も増えそうな感じがしました。
私は利用してます。
最後まで読んでいただきありがとうございました。
掲載のサンプルのライセンスは、
MIT Licenseで掲載しています。
では、また👋