2019年初頭、mozcをVS2019(VC2019)Previewでビルドしてみた
- キーボードからの日本語入力は無事できるところまで来た。
- 手書き入力は常にエラー表示になって使えない。
- Debugビルドができない。Releaseビルドだけ。
- unittestで2件ほど失敗する。
- Windows 10 Pro 64bit
- 別にHome Editionでも問題ないと思います。
- 32bit版Windowsではビルド不可とmozcの説明にあり。最近はさすがに減ってきたと思いますけど。
- CPU: AMD A8-3820。
- 6年位前に買ったパソコンゆえそろそろ新調したいところ。
- 自分でもやってみたいという方は CPU BOSS あたりでご自身のパソコンのCPUと性能比較を確認しておきましょう。
- SSD: 512GB。SUNEAST SSD SE800 512GB。
- 最近気の迷いで買っちまったやつ。この頃SSDが値下げ傾向らしいですよ。
- Visual Studio 2019 Preview
- 設置時、Visual C++っぽいところ一箇所にチェックを入れてください。初期設定のままでは入りません。C++指定時に自動でつく対象だけ設置すればOKです。
- Qt 5.12.0
- この版から「MSVC 2015 32bit」がなくなっていますが、「MSVC 2017 32bit」で代用できるようです。本件では64bitのQtは使えません。
- Gitやコマンドプロンプトなど
- それらが使える知識がある前提の文章なので念の為。基本的な知識だけでいいです。
- それらが使える知識がある前提の文章なので念の為。基本的な知識だけでいいです。
概説
要点1: depot_tools.zip版の GYP は Preview版VS2019 に未対応
要点2: mozcから外部のGYPを指定できそうだが実はできない
gyp_dir = os.path.abspath(options.gypdir)
gyp_command = [os.path.join(gyp_dir, 'gyp')]
else:
# Use third_party/gyp/gyp unless 'gypdir' option is specified. This is
gyp_dir = os.path.abspath(options.gypdir)
if IsWindows():
gyp_command = [os.path.join(gyp_dir, 'gyp.bat')]
else:
gyp_command = [os.path.join(gyp_dir, 'gyp')]
else:
# Use third_party/gyp/gyp unless 'gypdir' option is specified. This is
要点3: Preview版VS2019は最新版GYPでも補足はしない
set GYP_MSVS_OVERRIDE_PATH=C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Preview
set GYP_MSVS_VERSION=2017
要点4: mozcにVisual C++ 2015決め打ちのところがある
if IsWindows():
gyp_options.extend(['-G', 'msvs_version=2015'])
if (target_platform == 'Linux' and
if IsWindows():
gyp_options.extend(['-G', 'msvs_version=2017'])
os.environ['GYP_MSVS_OVERRIDE_PATH'] = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Preview'
os.environ['GYP_MSVS_VERSION'] = '2017'
if (target_platform == 'Linux' and
要点5: mozc の C++ コードに問題を抱えているところがある
C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\VC\Tools\MSVC\14.20.27027\include\xutility(3248): error C2678: 二項演算子 '*': 型 'const _InIt' の左オペランドを扱う演算子が見つかりません (または変換できません) (新しい動作; ヘルプを参照)。
with
[
_InIt=mozc::IteratorAdapter<const int *,mozc::storage::louds::`anonymous-namespace'::ZeroBitAdapter>
]
value_type operator*() {
return adapter_(iter_);
}
value_type operator*() const {
return adapter_(iter_);
}
要点6: GYPがコマンドライン引数にほぼ問答無用で os.path.normpath をかけている
--input=../../data/dictionary_oss/dictionary00.txt
pos_matcher:32:..\..\out_win\Debug\gen\data_manager\chromeos\pos_matcher.data
data\dictionary_oss\dictionary00.txt
out_win\Debug\gen\data_manager\chromeos\pos_matcher.data
pylib/gyp/msvs_emulation.py | 40 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 38 insertions(+), 2 deletions(-)
diff --git a/pylib/gyp/msvs_emulation.py b/pylib/gyp/msvs_emulation.py
index 63d40e63..4ff51a5e 100644
--- a/pylib/gyp/msvs_emulation.py
+++ b/pylib/gyp/msvs_emulation.py
@@ -23,10 +23,45 @@ try:
except NameError:
basestring = str
+enable_normpath = 1 # 0 to False, 1 to True, but cannot build with 1? 0? or both?
windows_quoter_regex = re.compile(r'(\\*)"')
+def OptionRemainedNormpathForRspFile(arg):
+ """Apply os.path.normpath except the option declaration part.
+ Problematic case is '--optionname=../relpath/arg' or
+ '-o../relpath/arg'. When the name of option and argument for the
+ option is separated with spaces, the caller of this function split them
+ and no problems occur. Also, if arg doesn't have relative path,
+ os.path.normpath works without problems.
+
+ We should consider arg is 'some_option:param_for_option:path' style.
+ os.path.normpath('a:b:..\\just_child_relative_path') returns same but
+ os.path.normpath('a:b:..\\..\\more_deep_relative_path') returns
+ 'more_deep_relative_path'. We should avoid it too.
+ """
+ if not enable_normpath:
+ return arg
+ if arg.find('\\') == -1:
+ # Because that can be a full of, or a part of URL.
+ return arg
+ if arg[-1:] == '/':
+ # Because that can be required last slash to join another string.
+ # os.path.normpath('somepath/') -> 'somepath'
+ return arg
+ mo = re.search(r'--[^=]*=|-[^.-]?', arg)
+ if mo and mo.start() == 0:
+ return mo.group(0) + OptionRemainedNormpathForRspFile(arg[mo.end(0):])
+ rpos = arg.rfind(':')
+ if rpos == len(arg) - 1:
+ # Because os.path.normpath('') returns '.'
+ return arg
+ if rpos >= 0:
+ return arg[:rpos + 1] + os.path.normpath(arg[rpos + 1:])
+ return os.path.normpath(arg)
+
+
def QuoteForRspFile(arg):
"""Quote a command line argument so that it appears as one argument when
processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for
@@ -38,8 +73,9 @@ def QuoteForRspFile(arg):
# use that function to handle command line arguments.
# Use a heuristic to try to find args that are paths, and normalize them
+ # NOTE: if arg doesn't contain backslashs, no changes will be made.
if arg.find('/') > 0 or arg.count('/') > 1:
- arg = os.path.normpath(arg)
+ arg = OptionRemainedNormpathForRspFile(arg)
# For a literal quote, CommandLineToArgvW requires 2n+1 backslashes
# preceding it, and results in n backslashes + the quote. So we substitute
@@ -71,7 +107,7 @@ def EncodeRspFileList(args):
if not args: return ''
if args[0].startswith('call '):
call, program = args[0].split(' ', 1)
- program = call + ' ' + os.path.normpath(program)
+ program = call + ' ' + OptionRemainedNormpathForRspFile(program)
else:
program = os.path.normpath(args[0])
return program + ' ' + ' '.join(QuoteForRspFile(arg) for arg in args[1:])
これを diff ファイルとしてどうにかこうにかして適用。
ビルドできない問題点としてはこれで全部のはず。あと、(gyp)/pylib/gyp/generator/ninja.jp で /showIncludes が指定されているせいでログファイルが14MBとかになっちゃうのであらかじめこれらを削っておくのが吉。
だんだん疲れてきたので記事が投げっぱなしになってますが、だいたいこれで通るはず。幸運を祈る。