NeovimからVSCodeのlaunch.jsonやtasks.jsonでタスクを実行
概要
Visual Studio Code(以下VSCode)ではタスク管理に.vscode/tasks.jsonを、起動やデバッグに.vscode/launch.jsonへ書いて実行することが多いと思います。当然ですがNeovimではこれらのタスクをそのまま実行することはできませんので、Neovimを使う際はmakefileやシェルスクリプトなどにタスクを書いているのではないでしょうか。
VSCodeとNeovim(拡張機能でない)を同時に使用している筆者のような場合や共同プロジェクトの場合、NeovimからVSCodeのタスクランナーが使用できれば管理しやすいのではないかと思います。
今回は、NeovimからVSCode用のタスクを実行する方法を紹介します。
使用するプラグインなど
- "mfussenegger/nvim-dap"
Debug Adapter Protocolでデバッグを可能にします。拡張機能で.vscode/launch.jsonを読み込めます。 - "rcarriga/nvim-dap-ui"
(省略可) nvim-dapをGUI上で操作可能にします。あるととても便利です。 - "EthanJWright/vs-tasks.nvim"
VSCode用のタスクランナー(task.json)を実行します。- "nvim-lua/popup.nvim"
- "nvim-lua/plenary.nvim"
- "nvim-telescope/telescope.nvim"
vs-tasks.nvimの依存です。(ポップアップウィンドウ出したりなど)
- launchに使用するための各言語のLSP(C++ならcodelldbなど) LSPについてはこちらを参照してください。
インストール
筆者はプラグインマネージャにLazy.nvimを使用しているのでその例です。
またDapの設定はC++で使用するCodeLLDBの設定です。
{ { -- debug "mfussenegger/nvim-dap", dependencies = { -- 無くても可 "rcarriga/nvim-dap-ui", }, config = function() -- 設定1 end, }, { -- vscode tasks "EthanJWright/vs-tasks.nvim", dependencies = { "nvim-lua/popup.nvim", "nvim-lua/plenary.nvim", "nvim-telescope/telescope.nvim", }, }, -- 設定2 }
設定
nvim-dap.nvim(設定1)
config = function() -- 設定1 local dap = require("dap") dap.adapters = { -- LSPの設定 lldb = { type = "server", port = "${port}", executable = { command = vim.fn.stdpath("data") .. "/mason/packages/codelldb/extension/adapter/codelldb", args = { "--port", "${port}" }, }, }, } dap.configurations = { cpp = {}, } -- -- キーマップの登録 vim.keymap.set("n", "<F5>", dap.continue, { desc = "Debug: Start/Continue" }) vim.keymap.set("n", "<F1>", dap.step_into, { desc = "Debug: Step Into" }) vim.keymap.set("n", "<F2>", dap.step_over, { desc = "Debug: Step Over" }) vim.keymap.set("n", "<F3>", dap.step_out, { desc = "Debug: Step Out" }) vim.keymap.set("n", "<leader>b", dap.toggle_breakpoint, { desc = "Debug: Toggle Breakpoint" }) vim.keymap.set("n", "<leader>B", function() dap.set_breakpoint(vim.fn.input("Breakpoint condition: ")) end, { desc = "Debug: Set Breakpoint" }) -- .vscode/launch.jsonの読み込み require("dap.ext.vscode").load_launchjs(nil, { lldb = { "c", "cpp", "" } }) -- DAP-UIの設定(省略可) local dapui = require("dapui") -- 任意 vim.fn.sign_define("DapBreakpoint", { text = "", texthl = "DapBreakpointTextHl" }) vim.fn.sign_define("DapStopped", { text = "", texthl = "DapStoppedTextHl" }) dapui.setup({ controls = { element = "repl", enabled = true, icons = { pause = "⏸", play = "▶", step_into = "⏎", step_over = "⏭", step_out = "⏮", step_back = "b", run_last = "▶▶", terminate = "⏹", disconnect = "⏏", enabled = "●", }, }, element_mappings = {}, expand_lines = true, floating = { border = "single", mappings = { close = { "q", "<Esc>" }, }, }, force_buffers = true, icons = { collapsed = "", current_frame = "", expanded = "", }, layouts = { { elements = { { id = "scopes", size = 0.25, }, { id = "breakpoints", size = 0.25, }, { id = "stacks", size = 0.25, }, { id = "watches", size = 0.25, }, }, position = "left", size = 40, }, { elements = { { id = "repl", size = 0.5, }, { id = "console", size = 0.5, }, }, position = "bottom", size = 10, }, }, mappings = { edit = "e", expand = { "<CR>", "<2-LeftMouse>" }, open = "o", remove = "d", repl = "r", toggle = "t", }, render = { indent = 1, max_value_lines = 100, }, }) vim.keymap.set("n", "<F7>", dapui.toggle, { desc = "Debug: See last session result." }) dap.listeners.after.event_initialized["dapui_config"] = dapui.open dap.listeners.before.event_terminated["dapui_config"] = dapui.close dap.listeners.before.event_exited["dapui_config"] = dapui.close end
require("dap.ext.vscode").load_launchjs(nil, { lldb = { "c", "cpp", "" } })ですが、lldbの部分、VSCodeのCodelldbのtype名はlldbだったのでそちらに合わせます。
vs-tasks.nvim(設定2)
ショートカットからtaskを実行出来るようにします。
-- 設定2 vim.cmd([[ nnoremap <Leader>ta :lua require("telescope").extensions.vstask.tasks()<CR> nnoremap <Leader>ti :lua require("telescope").extensions.vstask.inputs()<CR> nnoremap <Leader>th :lua require("telescope").extensions.vstask.history()<CR> nnoremap <Leader>tl :lua require('telescope').extensions.vstask.launch()<cr> ]]),
実行
F5でデバッグ(launch.json)の選択・開始、(leader)taでタスク(tasks.json)を実行出来ます。デバッガが不要で起動だけするなら(leader)tlで可能です。
参考
VSCodeとWindows TerminalでDeveloper PowerShellを起動する
TL;DR
Windows Terminal
C:\Program Files\PowerShell\7\pwsh.exe -noe -c "&{Import-Module 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/Tools/Microsoft.VisualStudio.DevShell.dll'; Enter-VsDevShell 677f3018}"
Visual Studio Code
"terminal.integrated.profiles.windows": {
"Developer PowerShell for VS 2019": {
"source": "PowerShell",
"icon": "terminal-powershell",
"font": {
"face": "CaskaydiaCove Nerd Font"
},
"args": [
"-noe",
"-c",
"&{Import-Module 'C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/Tools/Microsoft.VisualStudio.DevShell.dll'; Enter-VsDevShell 677f3018}"
]
}
}
Effekseerをemscriptenで動かす その2 ファイルローダ編
前回、ファイルローダがどうのこうの言ってから2ヶ月が経過してしまった。
Effekseerの標準のテクスチャローダではテクスチャを読み込むことができない。が、インターフェースが提供されているので各々で実装することはできる。
NenEngineに実装されている。
インターフェースは以下のようになった。
https://github.com/Astomih/NenEngine/blob/main/src/Nen/Effect/CustomTextureLoader.hgithub.com
#include <Effekseer.h> class CustomTextureLoader : public Effekseer::TextureLoader { private: ::Effekseer::Backend::GraphicsDevice* graphicsDevice_ = nullptr; public: CustomTextureLoader(::Effekseer::Backend::GraphicsDevice* graphicsDevice) : graphicsDevice_(graphicsDevice) {} ~CustomTextureLoader() = default; public: Effekseer::TextureRef Load(const EFK_CHAR* path, Effekseer::TextureType textureType) override; void Unload(Effekseer::TextureRef data) override {} }
実装は以下。
https://github.com/Astomih/NenEngine/blob/main/src/Nen/Effect/CustomTextureLoader.cppgithub.com
#include "CustomTextureLoader.h" #include <Nen.hpp> #include <EffekseerRenderer/EffekseerRendererGL.MaterialLoader.h> #include <EffekseerRenderer/EffekseerRendererGL.RendererImplemented.h> #include <EffekseerRenderer/GraphicsDevice.h> #include <fstream> #include <SDL.h> #include <SDL_image.h> Effekseer::TextureRef CustomTextureLoader::Load(const EFK_CHAR* path, Effekseer::TextureType textureType) { std::array<char, 260> path8; Effekseer::ConvertUtf16ToUtf8(path8.data(), static_cast<int32_t>(path8.size()), path); SDL_Surface* surf = IMG_Load(path8.data()); if (!surf) { nen::Logger::Error("Failed to load \"%s\". IMG_Error: %s", std::string(path8.data()), IMG_GetError()); } ::SDL_LockSurface(surf); auto formatbuf = ::SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888); formatbuf->BytesPerPixel = 4; auto imagedata = ::SDL_ConvertSurface(surf, formatbuf, 0); SDL_UnlockSurface(surf); // Generate a GL texture GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf->w, surf->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, imagedata->pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); std::string pathStr = path8.data(); auto backend = static_cast<EffekseerRendererGL::Backend::GraphicsDevice*>(graphicsDevice_) ->CreateTexture(texture, false, [texture, pathStr]() -> void { glDeleteTextures(1, &texture); }); auto textureData = Effekseer::MakeRefPtr<Effekseer::Texture>(); textureData->SetBackend(backend); nen::Logger::Info("Loaded \"%s\".", pathStr); SDL_FreeSurface(surf); SDL_FreeSurface(imagedata); SDL_FreeFormat(formatbuf); return textureData; }
基本的には、画像やマテリアルをこちら側で読み込み、各種APIの登録を済ませてからCreateTextureを呼ぶ。
Effekseerをemscriptenで動かす
はじめに
やり方としては2種類ある。(多分)
- jsファイルとして出力し、Three.jsなどのJavaScript用グラフィックスライブラリを使用して動かす
- 静的リンクライブラリとして出力し、既存のC++プロジェクトとともにリンクして直接動かす
今回は、2番の方法を紹介する。なお、1番の方法に関してはわざわざビルドする必要すら無く、ビルド済みのjsファイルが配布されているのでそちらを利用すれば良い。
2番の方法はやり方が載っていなかったので表示するまでに2日くらい掛かってしまった。
静的リンクライブラリのビルド
まずは普通に先程のリポジトリをcloneする。
git clone https://github.com/effekseer/EffekseerForWebGL cd EffekseerForWebGL git submodule update --init
src内にあるCMakeLists.txtを開き、
# Set output file extension
set(CMAKE_EXECUTABLE_SUFFIX ".js")
# Add build settings
add_executable(effekseer.core ${effekseer_src})
という記述を消し(このままビルドすると上記のjsファイルをビルド出来る)、代わりに
# Add build settings
add_library(effekseer STATIC ${effekseer_src})
としておく。 あとは
emsdk install latest emsdk activate latest emcmake cmake . emmake make
とすればlibeffekseer.aが出力される
ファイルローダ
src/cpp/main.cppのような記述でなくても、本家のリポジトリのexampleのように記述すれば動く。しかし、ファイル読み込みについてはどうやら既存のローダでは読み込めないようなので自作する必要がある。
そこで、src/cpp/CustomFile.hを参考にした。EM_ASM内のコードが機能しなかったので、素直にstd::fstreamなどで読み込んだ。
テクスチャに関しては、読み込んだファイルをSDL_Imageを介して無理やり読み込ませた。マテリアルの読み込み等についてはまだ実装していない。
文字で説明なんかせずにコードを載せれば良いのだろうが、この辺りは未だ調査中なので不明な点が多い。
判明次第随時更新するつもりだが、とりあえず今回はこのへんで。
OpenAL実装ライブラリ、MojoAL
オーディオAPIとして有名なOpenALは、扱いやすくて人気があるがライセンスがLGPLなので扱いが難しい。
そこで近年、zlibライセンスで開発されているオープンソースのSDL2上でOpenALのAPIが実装された。
開発したのはSDLの製作者でもあるRyan C Gordon氏。
使用するにはSDL2.0.6以降のオーディオAPIが必要となる。
SDL2の機能を使用しているため、対応している音声フォーマットはwavファイルのみである。
OpenAL自体はフォーマットの規定をしていない。何らかの形で音声データを読み込んで渡すだけである。
この実装の特徴は、SDL2に対応したプラットフォームならどこでも使える点(OpenAL自体移植されまくっているが、SDL2もモバイルからWebにゲーム機まで色々)とスレッドセーフな点だと思われる。
オーディオについて詳しくないので、詳細は上記のホームページとソースコードのコメントを読む方が良い。
3Dサウンドを単にミキシングするだけの私にとってはFMODなどのオーディオエンジンとか使うよりも敷居が低いし扱いやすいのでとてもありがたいライブラリ。
追記:
AL_ORIENTATIONでListenerの向きを変更できるはずだが、音源のLRがスムーズに切り替わらない。
SDL2のオーディオAPIか、OSか、ドライバか、はたまたMojoAL自体の問題だと思われるが、あまり使えそうにない。なお、純正のOpenAL実装で試したところこちらは上手く鳴ったので、プログラム側の問題ではないと思われる。
DockerでFactorioサーバーを建てるメモ
結論
sudo mkdir -p /opt/factorio sudo chown 845:845 /opt/factorio sudo docker run -d \ -p 34197:34197/udp \ -p 27015:27015/tcp \ -v /opt/factorio:/factorio \ --name factorio \ --restart=always \ factoriotools/factorio
何故わざわざDockerで?
Factorioサーバーを建てるためには、公式サイトからHeadless Serverをダウンロードして起動する必要がある。
Factorioサーバー起動時にはglibc2.18が必要となるが、CentOS7では標準でglib2.17が使用されている。
glibcとはGNUのC言語実装ライブラリであるが、安易にglibcを導入するとカーネルが起動しなくなる恐れがある(なった)。
そのため、次の手段としてdockerを使用することにした。
サーバー構築
以下で紹介するのはCentOS7での方法である。
ポート開放
Dockerを入れる前に、ポートの開放とファイアウォールを通す必要がある。Factorioで使用するポートは34197(UDP)だ。
# firewall-cmd --add-port=34197/udp --zone=public --permanent
Dockerの導入
# yum -y install docker
# systemctl start docker
Factorioイメージの導入
今回は一番メンテナンスがされているfactoriotoolsのコンテナを使用する。
https://hub.docker.com/r/factoriotools/factorio/
公式のガイドに沿って進めれば基本的に問題ない。
sudo mkdir -p /opt/factorio sudo chown 845:845 /opt/factorio sudo docker run -d \ -p 34197:34197/udp \ -p 27015:27015/tcp \ -v /opt/factorio:/factorio \ --name factorio \ --restart=always \ factoriotools/factorio
ただし、気をつけるべき点は上記のままでは最新版になってしまう。安定版では無いということで、すなわち公式サイトのExperimentsを含む最新のバージョンのことである。
例えば執筆時点のFactorioの最新バージョンは1.1.21だが、Experiments版は1.1.25だ。もし1.1.21のサーバーを建てるには、
sudo mkdir -p /opt/factorio sudo chown 845:845 /opt/factorio sudo docker run -d \ -p 34197:34197/udp \ -p 27015:27015/tcp \ -v /opt/factorio:/factorio \ --name factorio \ --restart=always \ factoriotools/factorio:1.1.25
のように末尾に「:」とバージョンを書けば良い。
実行
ドキュメントの通り、
# docker start factorio
停止
# docker stop factorio
動作確認(log)
# docker logs factorio
独自の問題?
私の環境だけの問題かもしれないが、初回実行時にPermission deniedが出た。ディレクトリの権限を変えたりなど色々試したが駄目だったので、一時的にセキュリティを外した。
一時的に外すためには、
# setenforce 0
とする。
ただしこれは最後の手段であるのでおすすめしない。
サーバーを建て終わったら、
# setenforce 1
でSELinuxを再度有効化しておく。
VisualStudioのプロジェクトをコマンドラインからビルドする
新年です。だからなにって訳ではないですが。
最近、コマンドラインからビルドしたいなーと思っていて調べたところMSBuildというのが使えるらしい。 はてなブログタグに登録されているくらい。 場所は
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\
にある。(2019は任意のバージョン) 面倒なのでとりあえず環境変数に打ち込んだ。
MSBuild.exeをターミナルから実行できるのを確認したら、↓こんな感じにビルドすることができる。
MSBuild C:/hoge/fuga/piyo.sln /t:build /p:Configuration=Debug;Platform="x86"
ソリューションに複数のプロジェクトがある場合は/t:にbuild、任意のプロジェクトをビルドしたいときにはプロジェクト名に変更することでビルド可能。
その他ConfigurationとPlatformは見ての通りで、これでおそらく最低限はビルド可能になる。
それ以外のコマンドは公式ドキュメントを読んでください。結局デバッガなどを使用するとなるとVisual Studioを起動する羽目にはなると思います。
docs.microsoft.com
毎回打つのは面倒なのでaliasに登録してるんですが、makefileみたいになんかもっとスマートに出来ると思うのでよければ教えて下さい。
追記:調べたところ、nmakeというのがやっぱりあるらしい。書き方もmakefileに似ているので、今後書く予定。