IfcOpenShellをコンパイルした件

# やりたかったこと
以前、[IfcOpenShell pythonバインディングのことはじめ](https://qiita.com/Takashi_Kasuya/items/dced9fc3d8869e88ce6c)で、condaでのIfcOpenShellのPythonバインディングを入れる解説をしましたが、何度やってもうまくいったりいかなかったりします。

最近はC++を触ることも多いので、この際、ソースからビルドしてみることにしました。環境はWindows 10、Visual Studio 2019 Communityです。これをやると、自動的にPythonバインディングも生成されます。

# 準備
まずは、ifcOpenShellで[Githubのサイト](https://github.com/IfcOpenShell/IfcOpenShell)で最新のコードをダウンロードしておきます。同サイトに要件なども書いてあります。Visual Studioの他にCMakeもインストールしてパスを通しておきましょう。作業を進めるには、Developer Command Promptが必要です。Visual Studioをインストールしたらついてくると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51703/7f6810ee-f4e8-7730-5154-9d10c634fb15.png)

ダウンロードして解凍したら、上記のコマンドプロンプトを使って、当該フォルダに移動します。その中にwinというフォルダがあるので、そこまで移動します。

# コンパイル
詳細はwinフォルダのreadme.mdに書いてあるのですが、ここでは1つのやり方を示しておきます。ビルドに関して、IfcOpenShellは64ビットが推奨されるとのことです。理由はIFCは容易に2Gを超えるからとあります。また、何も指定しないとDebug用のビルドになりますが、今回はReleaseとしています。

最初のbuild-deps.cmd以外は、最初のパラメータがキャッシュ変数で保存されるはずなのですが、全て指定しないとうまくいきませんでした。

```
cd win
# 依存ファイルのダウンロード、ビルド
build-deps.cmd vs2019-x64 Release
# CMakeによりVisual Studioのソリューションファイルを作成
run-cmake.bat vs2019-x64 Release
# コマンドでVisual Studioのソリューションのビルド
build-ifcopenshell.bat vs2019-x64 Release
# フォルダにインストール
install-ifcopenshell.bat vs2019-x64 Release
```

うまくいくと、`IfcOpenShell`のフォルダに`_installed-vs2019-x64`が生成されており、`bin/include/lib`があることが分かります。必要に応じてDebugも作るとよいと思います。


# サンプルプログラムの実行
インストールされたライブラリを使ってみます。今回はCMakeでコンパイルしてみようと思います。ただ、CMakeで生成しているにも関わらず、CMakeのConfigがありません。ドキュメントにも手動で参照してねと書いてあります。なんて面倒な、、、

## CMakeする
今回は汎用的にCMakeでビルドしてみます。いくつか注意することがあって、上記のインストールプロセスで、CRTの設定が静的リンクをすることになっています。[ここ](https://sgryjp.gitlab.io/posts/2017/2017-05-27/) を参考に切り替えを自動化するようにしておきます。対象するソースコードは`main.cpp`です。

```CMakeLists.txt
project(main)
cmake_minimum_required(VERSION 3.14)

# Visual Studioでのコンパイル時は、CRTライブラリを静的リンクさせる
if(MSVC)
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_DEBUG          ${CMAKE_CXX_FLAGS_DEBUG})
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_MINSIZEREL     ${CMAKE_CXX_FLAGS_MINSIZEREL})
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE        ${CMAKE_CXX_FLAGS_RELEASE})
    string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
    string(REPLACE "/W3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /EHsc")
    set(CMAKE_CXX_FLAGS_DEBUG   "${CMAKE_CXX_FLAGS_DEBUG}   /DBUILD_TYPE=\\\"Debug\\\"")
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /DBUILD_TYPE=\\\"Release\\\"")
endif(MSVC)


set(IFCOPENSHELL_ROOT "IfcOpenShellのインストールフォルダ")
find_package(BOOST REQUIRED)

add_executable(main main.cpp)

target_include_directories(main PRIVATE 
    ${IFCOPENSHELL_ROOT}\\include
    ${Boost_INCLUDE_DIRS}    
)
target_link_directories(main PRIVATE ${IFCOPENSHELL_ROOT}\\lib)

target_link_libraries(main PRIVATE IfcGeom IfcGeom_ifc2x3 IfcGeom_ifc4 IfcParse)
```

以下はサンプルプログラムを少し変えたものです。
対象のIFCファイルから、IfcBuildingElementを抜いてきて、IfcWindowに該当するものは面積を計算しています。デバッグコンソールが文字化けするので、対処するためのコードを入れています。

```main.cpp
#include <iostream>
#include <string>  
#include <windows.h>

#define IfcSchema Ifc2x3

#include "ifcparse/IfcFile.h"
#include "ifcparse/Ifc2x3.h"

int main(int argc, char** argv) {

	std::string path = "";
	if (argc != 2) {
		path = "適当なifcファイルのパス";
	}
	else {
		path = argv[1];
	}

	// コマンドプロンプトのコードページを保存
	UINT prevCP = GetConsoleOutputCP();
	// コードページをUTF-8に変更
	SetConsoleOutputCP(CP_UTF8);


	// Redirect the output (both progress and log) to stdout
	Logger::SetOutput(&std::cout,&std::cout);

	// Parse the IFC file provided in argv[1]
	IfcParse::IfcFile file(path);
	if (!file.good()) {
		std::cout << "Unable to parse .ifc file" << std::endl;
		return 1;
	}


	IfcSchema::IfcBuildingElement::list::ptr elements = file.instances_by_type<IfcSchema::IfcBuildingElement>();

	std::cout << "Found " << elements->size() << " elements in " << path << ":" << std::endl;

	for (IfcSchema::IfcBuildingElement::list::it it = elements->begin(); it != elements->end(); ++it) {

		const IfcSchema::IfcBuildingElement* element = *it;
		IfcEntityInstanceData data = element->data();
		std::cout << element->data().toString() << std::endl;

		const IfcSchema::IfcWindow* window;
		if ((window = element->as<IfcSchema::IfcWindow>()) != 0) {
			if (window->hasOverallWidth() && window->hasOverallHeight()) {
				const double area = window->OverallWidth() * window->OverallHeight();
				std::cout << "The area of this window is " << area << std::endl;
			}
		}

	}
	// コードページを元に戻す(これをやらないとコマンドプロンプトを開き直すまでUTF-8のままになる)
	SetConsoleOutputCP(prevCP);
}
```

## Visual Studioでコンパイル
準備ができたら、cmakeをしてください。うまくいけば、`main.sln`ができていると思います。
なお、CMakeでの設定がうまくいっていれば、以下のように「ランタイムライブラリ」が`マルチスレッド(/MT)となっているはずです（Debugの場合は/MTd）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51703/3f418a9d-d2b3-8b08-ddcc-f2ec3eb51456.png)

以下が実行結果の例です。正しく対象のオブジェクトの属性が抽出できていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51703/17714717-ac49-eca8-b308-147db92e1486.png)