1baec84f2Syuangongji#!/usr/bin/env python3
2baec84f2Syuangongji#
3baec84f2Syuangongji# Check if find_package(Libevent COMPONENTS xxx) can get the correct library.
4baec84f2Syuangongji# Note: this script has only been tested on python3.
5baec84f2Syuangongji# Usage:
6baec84f2Syuangongji#   cd cmake-build-dir
7baec84f2Syuangongji#   cmake <options> .. && cmake --build .
8baec84f2Syuangongji#   python /path/to/test-export.py [static|shared]
9baec84f2Syuangongji
10baec84f2Syuangongjiimport sys
11baec84f2Syuangongjiimport os
12baec84f2Syuangongjiimport shutil
13baec84f2Syuangongjiimport platform
14baec84f2Syuangongjiimport subprocess
15baec84f2Syuangongjiimport tempfile
16baec84f2Syuangongji
17baec84f2Syuangongjiresults = ("success", "failure")
18baec84f2SyuangongjiFNULL = open(os.devnull, 'wb')
19baec84f2Syuangongjiscript_dir = os.path.split(os.path.realpath(sys.argv[0]))[0]
20baec84f2Syuangongji# working_dir is cmake build dir
21baec84f2Syuangongjiworking_dir = os.getcwd()
22baec84f2Syuangongjiif len(sys.argv) > 1 and sys.argv[1] == "static":
23baec84f2Syuangongji    link_type = sys.argv[1]
24baec84f2Syuangongjielse:
25baec84f2Syuangongji    link_type = "shared"
26baec84f2Syuangongji
27baec84f2Syuangongji
28baec84f2Syuangongjidef exec_cmd(cmd, silent):
29baec84f2Syuangongji    if silent:
30baec84f2Syuangongji        p = subprocess.Popen(cmd, stdout=FNULL, stderr=FNULL, shell=True)
31baec84f2Syuangongji    else:
32baec84f2Syuangongji        p = subprocess.Popen(cmd, shell=True)
33baec84f2Syuangongji    p.communicate()
34baec84f2Syuangongji    return p.poll()
35baec84f2Syuangongji
36baec84f2Syuangongji
37baec84f2Syuangongjidef link_and_run(link, code):
38baec84f2Syuangongji    """Check if the source code matches the library component.
39baec84f2Syuangongji
40baec84f2Syuangongji    Compile source code relative to one component and link to another component.
41baec84f2Syuangongji    Then run the generated executor.
42baec84f2Syuangongji
43baec84f2Syuangongji    Args:
44baec84f2Syuangongji        link: The name of component that the source code will link with.
45baec84f2Syuangongji        code: The source code related component name.
46baec84f2Syuangongji
47baec84f2Syuangongji    Returns:
48baec84f2Syuangongji        Returns 0 if links and runs successfully, otherwise 1.
49baec84f2Syuangongji    """
50baec84f2Syuangongji    exec_cmd("cmake --build . --target clean", True)
51*4e8e4613Syuangongji    arch = ''
52baec84f2Syuangongji    if platform.system() == "Windows":
53*4e8e4613Syuangongji        arch = '-A x64'
54baec84f2Syuangongji    cmd = 'cmake .. %s -DEVENT__LINK_COMPONENT=%s -DEVENT__CODE_COMPONENT=%s' % (
55*4e8e4613Syuangongji        arch, link, code)
56baec84f2Syuangongji    if link_type == "static":
57baec84f2Syuangongji        cmd = "".join([cmd, " -DLIBEVENT_STATIC_LINK=1"])
58baec84f2Syuangongji    r = exec_cmd(cmd, True)
59baec84f2Syuangongji    if r == 0:
60baec84f2Syuangongji        r = exec_cmd('cmake --build .', True)
61baec84f2Syuangongji        if r == 0:
62baec84f2Syuangongji            r = exec_cmd('ctest', True)
63baec84f2Syuangongji    if r != 0:
64baec84f2Syuangongji        r = 1
65baec84f2Syuangongji    return r
66baec84f2Syuangongji
67baec84f2Syuangongji# expect  0:success 1:failure
68baec84f2Syuangongjidef testcase(link, code, expect):
69baec84f2Syuangongji    r = link_and_run(link, code)
70baec84f2Syuangongji    if link == "":
71baec84f2Syuangongji        link = "all"
72baec84f2Syuangongji    if code == "":
73baec84f2Syuangongji        code = "all"
74baec84f2Syuangongji    if r != expect:
75baec84f2Syuangongji        print('[test-export] fail: link %s and run %s expects %s but gets %s.' %
76baec84f2Syuangongji              (link, code, results[expect], results[r]))
77baec84f2Syuangongji        sys.exit(1)
78baec84f2Syuangongji    else:
79baec84f2Syuangongji        print('[test-export] success: link %s and run %s expects and gets %s.' %
80baec84f2Syuangongji              (link, code, results[r]))
81baec84f2Syuangongji
82baec84f2Syuangongji# Dependency relationships between libevent libraries:
83baec84f2Syuangongji#   core:        none
84baec84f2Syuangongji#   extra:       core
85baec84f2Syuangongji#   pthreads:    core,pthread
86baec84f2Syuangongji#   openssl:     core,openssl
87baec84f2Syuangongjidef test_group():
88baec84f2Syuangongji    testcase("core", "core", 0)
89baec84f2Syuangongji    testcase("extra", "extra", 0)
90baec84f2Syuangongji    testcase("openssl", "openssl", 0)
91baec84f2Syuangongji    testcase("", "", 0)
92baec84f2Syuangongji    testcase("extra", "core", 0)
93baec84f2Syuangongji    testcase("openssl", "core", 0)
94baec84f2Syuangongji    testcase("core", "extra", 1)
95baec84f2Syuangongji    testcase("core", "openssl", 1)
96baec84f2Syuangongji    testcase("extra", "openssl", 1)
97baec84f2Syuangongji    testcase("openssl", "extra", 1)
98baec84f2Syuangongji    if platform.system() != "Windows":
99baec84f2Syuangongji        testcase("pthreads", "pthreads", 0)
100baec84f2Syuangongji        testcase("pthreads", "core", 0)
101baec84f2Syuangongji        testcase("core", "pthreads", 1)
102baec84f2Syuangongji        testcase("extra", "pthreads", 1)
103baec84f2Syuangongji        testcase("pthreads", "extra", 1)
104baec84f2Syuangongji        testcase("pthreads", "openssl", 1)
105baec84f2Syuangongji        testcase("openssl", "pthreads", 1)
106baec84f2Syuangongji
107baec84f2Syuangongji
108baec84f2Syuangongjidef config_restore():
109baec84f2Syuangongji    if os.path.isfile("tempconfig") and not os.path.isfile("LibeventConfig.cmake"):
110baec84f2Syuangongji        os.rename("tempconfig", "LibeventConfig.cmake")
111baec84f2Syuangongji
112baec84f2Syuangongji
113baec84f2Syuangongjidef config_backup():
114baec84f2Syuangongji    if os.path.isfile("tempconfig"):
115baec84f2Syuangongji        os.remove("tempconfig")
116baec84f2Syuangongji    if os.path.isfile("LibeventConfig.cmake"):
117baec84f2Syuangongji        os.rename("LibeventConfig.cmake", "tempconfig")
118baec84f2Syuangongji
119baec84f2Syuangongji
120baec84f2Syuangongjishutil.rmtree(os.path.join(script_dir, "build"), ignore_errors=True)
121baec84f2Syuangongji
122baec84f2Syuangongji
123baec84f2Syuangongjidef run_test_group():
124baec84f2Syuangongji    os.chdir(script_dir)
125baec84f2Syuangongji    if not os.path.isdir("build"):
126baec84f2Syuangongji        os.mkdir("build")
127baec84f2Syuangongji    os.chdir("build")
128baec84f2Syuangongji    test_group()
129baec84f2Syuangongji    os.chdir(working_dir)
130baec84f2Syuangongji
131baec84f2Syuangongji
132baec84f2Syuangongjineed_exportdll = False
133baec84f2Syuangongjiif link_type == "shared" and platform.system() == "Windows":
134baec84f2Syuangongji    need_exportdll = True
135baec84f2Syuangongji
136baec84f2Syuangongji# On Windows, we need to add the directory containing the dll to the
137baec84f2Syuangongji# 'PATH' environment variable so that the program can call it.
138baec84f2Syuangongjidef export_dll(dir):
139baec84f2Syuangongji    if need_exportdll:
140baec84f2Syuangongji        os.environ["PATH"] += os.pathsep + dir
141baec84f2Syuangongji
142baec84f2Syuangongji
143baec84f2Syuangongjidef unexport_dll(dir):
144baec84f2Syuangongji    if need_exportdll:
145baec84f2Syuangongji        paths = os.environ["PATH"].split(os.pathsep)
146baec84f2Syuangongji        paths = list(set(paths))
147baec84f2Syuangongji        if dir in paths:
148baec84f2Syuangongji            paths.remove(dir)
149baec84f2Syuangongji        os.environ["PATH"] = os.pathsep.join(paths)
150baec84f2Syuangongji
151baec84f2Syuangongji
152baec84f2Syuangongjiprint("[test-export] use %s library" % link_type)
153baec84f2Syuangongji
154baec84f2Syuangongji# Test for build tree.
155baec84f2Syuangongjiprint("[test-export] test for build tree")
156baec84f2Syuangongjidllpath = os.path.join(working_dir, "bin", "Debug")
157baec84f2Syuangongjiconfig_restore()
158baec84f2Syuangongjios.environ["CMAKE_PREFIX_PATH"] = working_dir
159baec84f2Syuangongjiexport_dll(dllpath)
160baec84f2Syuangongjirun_test_group()
161baec84f2Syuangongjidel os.environ["CMAKE_PREFIX_PATH"]
162baec84f2Syuangongjiunexport_dll(dllpath)
163baec84f2Syuangongji
164baec84f2Syuangongji# Install libevent libraries to system path. Remove LibeventConfig.cmake
165baec84f2Syuangongji# from build directory to avoid confusion when using find_package().
166baec84f2Syuangongjiprint("[test-export] test for install tree(in system-wide path)")
167baec84f2Syuangongjiif platform.system() == "Windows":
168baec84f2Syuangongji    prefix = "C:\\Program Files\\libevent"
169baec84f2Syuangongji    dllpath = os.path.join(prefix, "lib")
170baec84f2Syuangongjielse:
171baec84f2Syuangongji    prefix = "/usr/local"
172baec84f2Syuangongjiexec_cmd('cmake -DCMAKE_INSTALL_PREFIX="%s" ..' % prefix, True)
173baec84f2Syuangongjiexec_cmd('cmake --build . --target install', True)
174baec84f2Syuangongjiconfig_backup()
175baec84f2Syuangongjios.environ["CMAKE_PREFIX_PATH"] = os.path.join(prefix, "lib/cmake/libevent")
176baec84f2Syuangongjiexport_dll(dllpath)
177baec84f2Syuangongjirun_test_group()
178baec84f2Syuangongjiunexport_dll(dllpath)
179baec84f2Syuangongjidel os.environ["CMAKE_PREFIX_PATH"]
180baec84f2Syuangongji
181baec84f2Syuangongji# Uninstall the libraries installed in the above steps. Install the libraries
182baec84f2Syuangongji# into a temporary directory. Same as above, remove LibeventConfig.cmake from
183baec84f2Syuangongji# build directory to avoid confusion when using find_package().
184baec84f2Syuangongjiprint("[test-export] test for install tree(in non-system-wide path)")
185baec84f2Syuangongjiexec_cmd("cmake --build . --target uninstall", True)
186baec84f2Syuangongjitempdir = tempfile.TemporaryDirectory()
187baec84f2Syuangongjicmd = 'cmake -DCMAKE_INSTALL_PREFIX="%s" ..' % tempdir.name
188baec84f2Syuangongjiexec_cmd(cmd, True)
189baec84f2Syuangongjiexec_cmd("cmake --build . --target install", True)
190baec84f2Syuangongjiconfig_backup()
191baec84f2Syuangongjios.environ["CMAKE_PREFIX_PATH"] = os.path.join(tempdir.name, "lib/cmake/libevent")
192baec84f2Syuangongjidllpath = os.path.join(tempdir.name, "lib")
193baec84f2Syuangongjiexport_dll(dllpath)
194baec84f2Syuangongjirun_test_group()
195baec84f2Syuangongjiunexport_dll(dllpath)
196baec84f2Syuangongjidel os.environ["CMAKE_PREFIX_PATH"]
197baec84f2Syuangongjiconfig_restore()
198baec84f2Syuangongji
199baec84f2Syuangongjiprint("[test-export] all testcases have run successfully")
200