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