#!/usr/bin/env python
############################################################################
# prepare.py
# Copyright (C) 2015 Belledonne Communications, Grenoble France
#
############################################################################
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
############################################################################
import argparse
import os
import re
import subprocess
import sys
import urllib
import urllib.request
import uuid
from logging import error, warning, info
from subprocess import Popen, PIPE
sys.dont_write_bytecode = True
sys.path.insert(0, 'submodules/cmake-builder')
try:
import prepare
except Exception as e:
error(
"Could not find prepare module: {}, probably missing submodules/cmake-builder? Try running:\n"
"git submodule sync && git submodule update --init --recursive".format(e))
exit(1)
class Win10Target(prepare.Target):
def __init__(self, arch, generator_platform = None):
prepare.Target.__init__(self, 'win10-' + arch)
current_path = os.path.dirname(os.path.realpath(__file__))
current_path = current_path.replace('\\', '/')
self.generator = 'Visual Studio 16 2019'
self.platform_name = generator_platform
self.config_file = 'configs/config-win10.cmake'
self.output = 'OUTPUT/win10-' + arch
self.external_source_path = os.path.join(current_path, 'submodules')
external_builders_path = os.path.join(current_path, 'cmake_builder')
self.additional_args = [
"-DLINPHONE_BUILDER_EXTERNAL_BUILDERS_PATH=" + external_builders_path
]
self.additional_args += ['-DCMAKE_CROSSCOMPILING=YES']
self.additional_args += ['-DCMAKE_SYSTEM_NAME=WindowsStore']
self.additional_args += ['-DCMAKE_SYSTEM_VERSION=10.0']
self.additional_args += ['-DCMAKE_SYSTEM_PROCESSOR=' + arch]
self.additional_args += ['-DCMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD=TRUE']
class Win10X86Target(Win10Target):
def __init__(self):
Win10Target.__init__(self, 'x86')
class Win10X64Target(Win10Target):
def __init__(self):
Win10Target.__init__(self, 'x64', 'x64')
class Win10ARMTarget(Win10Target):
def __init__(self):
Win10Target.__init__(self, 'ARM', 'ARM')
windows10_targets = {
'x86': Win10X86Target(),
'x64': Win10X64Target(),
'ARM': Win10ARMTarget()
}
linphone_builder_targets = ['linphone', 'ms2', 'bellesip']
class ComponentListAction(argparse.Action):
def __call__(self, parser, namespace, value, option_string=None):
if value:
if value not in linphone_builder_targets:
message = ("Invalid target: {0!r} (choose from {1}".format(
value, ', '.join([repr(target) for target in linphone_builder_targets])))
raise argparse.ArgumentError(self, message)
setattr(namespace, self.dest, value)
class Windows10Preparator(prepare.Preparator):
def __init__(self, targets=windows10_targets):
prepare.Preparator.__init__(self, targets)
self.veryclean = True
self.argparser.add_argument('-ac', '--all-codecs', help="Enable all codecs, including the non-free ones", action='store_true')
self.argparser.add_argument('--component', action=ComponentListAction, default='linphone', help="The component to build (default is 'linphone'). Can be one of: {0}.".format(', '.join([repr(target) for target in linphone_builder_targets])))
def parse_args(self):
prepare.Preparator.parse_args(self)
if self.args.all_codecs:
self.additional_args += ["-DENABLE_GPL_THIRD_PARTIES=YES"]
self.additional_args += ["-DENABLE_NON_FREE_CODECS=YES"]
self.additional_args += ["-DENABLE_AMRNB=YES"]
self.additional_args += ["-DENABLE_AMRWB=YES"]
self.additional_args += ["-DENABLE_G729=YES"]
self.additional_args += ["-DENABLE_GSM=YES"]
self.additional_args += ["-DENABLE_ILBC=YES"]
self.additional_args += ["-DENABLE_ISAC=YES"]
self.additional_args += ["-DENABLE_OPUS=YES"]
self.additional_args += ["-DENABLE_SILK=YES"]
self.additional_args += ["-DENABLE_SPEEX=YES"]
self.additional_args += ["-DENABLE_FFMPEG=YES"]
self.additional_args += ["-DENABLE_H263=YES"]
self.additional_args += ["-DENABLE_H263P=YES"]
self.additional_args += ["-DENABLE_MPEG4=YES"]
self.additional_args += ["-DENABLE_OPENH264=YES"]
self.additional_args += ["-DENABLE_VPX=YES"]
self.additional_args += ["-DENABLE_X264=NO"]
self.linphone_builder_target = self.args.component
if self.linphone_builder_target == 'ms2':
self.linphone_builder_target = 'ms2plugins'
self.additional_args += ["-DLINPHONE_BUILDER_TARGET=" + self.linphone_builder_target]
def clean(self):
prepare.Preparator.clean(self)
if os.path.isfile('SDK.sln'):
os.remove('SDK.sln')
if os.path.isfile('SDK.sdf'):
os.remove('SDK.sdf')
if os.path.isdir('WORK') and not os.listdir('WORK'):
os.rmdir('WORK')
if os.path.isdir('OUTPUT') and not os.listdir('OUTPUT'):
os.rmdir('OUTPUT')
def prepare(self):
self.download_nuget()
return prepare.Preparator.prepare(self)
def nuget_download_reporthook(self, count, block_size, total_size):
percent = int(count * block_size * 100 / total_size)
sys.stdout.write("\r-- Downloading nuget: %2d%%" % percent)
if percent == 100:
sys.stdout.write('\n')
sys.stdout.flush()
def download_nuget(self):
if not os.path.isdir('WORK'):
os.makedirs("WORK")
if not os.path.isfile('WORK/nuget.exe'):
urllib.request.urlretrieve("https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", "WORK/nuget.exe", reporthook = self.nuget_download_reporthook)
def git_version(self, path):
proc = subprocess.Popen(["git", "describe", "--always"], cwd=path, shell=False, stdout=subprocess.PIPE)
out, err = proc.communicate()
out = out.strip()
pos = out.find(b'-g')
if pos == -1:
return out
else:
return out[:out.find(b'-g')].replace(b'-', b'.')
def generate_vs_solution(self):
current_path = os.path.dirname(os.path.realpath(__file__))
current_path = current_path.replace('\\', '/')
guids = {}
sln_projects = ""
sln_confs = ""
other_sdks = []
build_type = 'Debug' if self.args.debug else 'Release'
builder_target = []
if self.linphone_builder_target == 'linphone':
linphone_version = self.git_version('submodules/linphone')
builder_target = [
('LinphoneTesterSDK', linphone_version),
('LinphoneSDK', linphone_version),
]
elif self.linphone_builder_target == 'ms2plugins':
ms2_version = self.git_version('submodules/mediastreamer2')
builder_target = [
('MS2TesterSDK', ms2_version),
]
elif self.linphone_builder_target == 'bellesip':
bellesip_version = self.git_version('submodules/belle-sip')
builder_target = [
('BelleSipTesterSDK', bellesip_version),
]
else:
return
vcxproj_platforms = {}
for platform in self.args.target:
if platform == 'x86':
vcxproj_platforms[platform] = 'Win32'
else:
vcxproj_platforms[platform] = platform
# Generate Visual Studio project to build the SDK for each platform
for platform in self.args.target:
guid = '{' + str(uuid.uuid4()).upper() + '}'
guids[platform] = guid
f = open("WORK/win10-{0}/cmake/ALL_BUILD.vcxproj".format(platform), 'r')
all_build_content = f.read()
f.close()
m = re.search("ToolsVersion=\"(.*)\" xmlns", all_build_content)
tools_version = m.group(1)
m = re.search("(.*)", all_build_content)
target_platform_version = m.group(1)
m = re.search("(.*)", all_build_content)
target_platform_min_version = m.group(1)
m = re.search("(.*)", all_build_content)
platform_toolset = m.group(1)
arm = '\n true' if platform == 'ARM' else ''
vcxproj = """
{build_type}
{vcxproj_platform}
{guid}
Windows Store
en-US
10.0
14.0{arm}
{target_platform_version}
{target_platform_min_version}
Win32Proj
{vcxproj_platform}
SDK_{platform}
Utility
false
Unicode
{platform_toolset}
$(Platform)\$(Configuration)\$(ProjectName)\
Building {platform} SDK
setlocal
cd {current_path}/WORK/win10-{platform}/cmake
if %errorlevel% neq 0 goto :cmEnd
C:
if %errorlevel% neq 0 goto :cmEnd
cmake.exe --build . --config $(Configuration)
if %errorlevel% neq 0 goto :cmEnd
:cmEnd
endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone
:cmErrorLevel
exit /b %1
:cmDone
if %errorlevel% neq 0 goto :VCEnd
%(AdditionalInputs)
{current_path}/WORK/win10-{platform}/Stamp/SDK-build
false
""".format(platform=platform, build_type=build_type, vcxproj_platform=vcxproj_platforms[platform], current_path=current_path, guid=guid, tools_version=tools_version, target_platform_version=target_platform_version, target_platform_min_version=target_platform_min_version, platform_toolset=platform_toolset, arm=arm)
f = open("WORK/win10-{0}/SDK_{0}.vcxproj".format(platform), 'w')
f.write(vcxproj)
f.close()
f = open("WORK/win10-{0}/SDK_{0}.rule".format(platform), 'w')
f.close()
sln_projects += \
"""Project("{{E8FB6309-B31E-4380-992C-BB1609B3EA00}}") = "SDK_{platform}", "WORK\win10-{platform}\SDK_{platform}.vcxproj", "{project_guid}"
EndProject
""".format(platform=platform, project_guid=guids[platform])
sln_confs += """\t\t{project_guid}.{build_type}|Win32.ActiveCfg = {build_type}|{vcxproj_platform}
\t\t{project_guid}.{build_type}|Win32.Build.0 = {build_type}|{vcxproj_platform}
""".format(project_guid=guids[platform], platform=platform, build_type=build_type, vcxproj_platform=vcxproj_platforms[platform])
# Generate Visual Studio projects to create a nuget packages
for target, version in builder_target:
guid = '{' + str(uuid.uuid4()).upper() + '}'
vcxproj = """
{build_type}
Win32
{guid}
Windows Store
en-US
10.0
14.0
{target_platform_version}
{target_platform_min_version}
Win32Proj
Win32
Nuget{target}
Utility
false
Unicode
{platform_toolset}
$(Platform)\$(Configuration)\$(ProjectName)\
Generating NuGet package for {target}
setlocal
cd {current_path}
if %errorlevel% neq 0 goto :cmEnd
C:
if %errorlevel% neq 0 goto :cmEnd
python.exe submodules/build/nuget.py -s OUTPUT -w WORK/NuGet{target} -cs CsWrapper -v {version} -t {target} {platforms}
if %errorlevel% neq 0 goto :cmEnd
cd {current_path}/OUTPUT
if %errorlevel% neq 0 goto :cmEnd
{current_path}/WORK/nuget.exe pack {current_path}/WORK/NuGet{target}/{target}.nuspec
:cmEnd
endlocal & call :cmErrorLevel %errorlevel% & goto :cmDone
:cmErrorLevel
exit /b %1
:cmDone
if %errorlevel% neq 0 goto :VCEnd
%(AdditionalInputs)
{current_path}/WORK/NuGet-build
false
""".format(platforms=' '.join(self.args.target), target=target, build_type=build_type, version=version, current_path=current_path, guid=guid, tools_version=tools_version, target_platform_version=target_platform_version, target_platform_min_version=target_platform_min_version, platform_toolset=platform_toolset)
f = open("WORK/NuGet{target}.vcxproj".format(target=target), 'w')
f.write(vcxproj)
f.close()
f = open("WORK/NuGet{target}.rule".format(target=target), 'w')
f.close()
project_dependencies = ""
for platform in self.args.target:
project_dependencies += """\t\t{project_guid} = {project_guid}
""".format(project_guid=guids[platform])
project_dependencies += """\t\t{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} = {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
"""
for sdk in other_sdks:
project_dependencies += """\t\t{project_guid} = {project_guid}
""".format(project_guid=guids[sdk])
other_sdks += [target]
guids[target] = guid
sln_projects += \
"""Project("{{E8FB6309-B31E-4380-992C-BB1609B3EA00}}") = "Nuget{target}", "WORK\\Nuget{target}.vcxproj", "{project_guid}"
\tProjectSection(ProjectDependencies) = postProject
{project_dependencies}\tEndProjectSection
EndProject
""".format(target=target, project_guid=guid, project_dependencies=project_dependencies)
sln_confs += \
"""\t\t{project_guid}.{build_type}|Win32.ActiveCfg = {build_type}|Win32
\t\t{project_guid}.{build_type}|Win32.Build.0 = {build_type}|Win32
""".format(project_guid=guid, build_type=build_type)
sln_confs += \
"""\t\t{project_guid}.{build_type}|Win32.ActiveCfg = {build_type}|Any CPU
\t\t{project_guid}.{build_type}|Win32.Build.0 = {build_type}|Any CPU
""".format(project_guid="{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}", build_type=build_type)
# Generate Visual Studio solution to build the SDK
sln = """Microsoft Visual Studio Solution File, Format Version 12.00
MinimumVisualStudioVersion = 10.0.40219.1
{sln_projects}Project("{{E8FB6309-B31E-4380-992C-BB1609B3EA00}}") = "CsWrapper", "CsWrapper\CsWrapper.csproj", "{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"
\tProjectSection(ProjectDependencies) = postProject
{project_dependencies}\tEndProjectSection
EndProject
Global
\tGlobalSection(SolutionConfigurationPlatforms) = preSolution
\t\t{build_type}|Win32 = {build_type}|Win32
\tEndGlobalSection
\tGlobalSection(ProjectConfigurationPlatforms) = postSolution
{sln_confs}\tEndGlobalSection
\tGlobalSection(SolutionProperties) = preSolution
\t\tHideSolutionNode = FALSE
\tEndGlobalSection
EndGlobal
""".format(sln_projects=sln_projects, sln_confs=sln_confs, build_type=build_type, project_dependencies=project_dependencies)
f = open('SDK.sln', 'w')
f.write(sln)
f.close()
info("You can now build the SDK.sln Visual Studio Solution.")
def main():
preparator = Windows10Preparator()
preparator.parse_args()
if preparator.check_environment() != 0:
preparator.show_environment_errors()
return 1
return preparator.run()
if __name__ == "__main__":
sys.exit(main())