Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » Developing U++ » Bazaar » Building TheIDE with using CMake
Building TheIDE with using CMake [message #32310] Sat, 07 May 2011 20:32 Go to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
I see more people interested using CMake to build U++ applications.

This topic was mentioned a long time ago, discussed on U++ meetings, etc. Therefore, now I want to show what was done (in cross-platform way).

For the example, I took TheIDE application, which can be used to build other U++ applications, after its build.
Tested on Windows (for MSC9 and TDM-GCC 4.6.1 compilers) for 4179 svn revision (release version).

How to build:
1. Get uppsrc sources, either:
a) Download src archive from SourceForge.
b) Using svn checkout:
svn checkout -r 4179 http://upp-mirror.googlecode.com/svn/trunk/uppsrc upp
svn export upp uppsrc

c) Export ide packages using existing TheIDE, by clicking on "Output mode" drop list and then clicking on "All" button for selected export project directory.

2. If you want to build with rainbow support, get rainbow sources using svn checkout:
svn checkout -r 4179 http://upp-mirror.googlecode.com/svn/trunk/rainbow rainbow
svn export rainbow uppsrc/rainbow

In case of 1.c) you also need to copy Painter package directory to exported uppsrc directory.

3. Rename all *.icpp files to *.cpp files inside following directories (or use CMakeLists.txt files for reference from ISRC_LIST list):
CtrlCore, CtrlLib, ide/Browser, ide/Builders, PdfDraw, plugin/bmp, plugin/gif, plugin/jpg, plugin/png, RichEdit, RichText, Web.
In case of 2.: Painter.

Also, you can use following RenameFiles.cmake script:
Toggle Spoiler


4. Copy CMakeLists.txt, *.cmake and build scripts files from attachments to source directory relative to uppsrc.

5. On Windows:
_start_here.bat
rem For GCC compiler
build.bat
rem For Microsoft Visual C++ 9.0 compiler
build_MSC9.bat
rem For rainbow support
build_MSC9_RAINBOW.bat


6. On FreeBSD (Linux, Unix, etc.):
./build.sh
# For rainbow support
./build_RAINBOW.sh


If all runs ok, ide(.exe) executable would be found inside install directories, relative to running commands.

CMakeLists.txt files and build scripts could be downloaded from attachments.
Full archive could be found here.

To note:
There were linking problems for MinGW version of build script with rainbow support, which I didn't include to attachment. The MSC9 version as is.

Edit:
Updated links to 4179 release version. But also applicable to 4193 release version.

[Updated on: Sun, 27 May 2012 22:19]

Report message to a moderator

Re: Building TheIDE with using CMake [message #32312 is a reply to message #32310] Sat, 07 May 2011 20:50 Go to previous messageGo to next message
Mindtraveller is currently offline  Mindtraveller
Messages: 916
Registered: August 2007
Location: Russia, Moscow rgn.
Experienced Contributor

So you managed to build TheIDE with Digital Mars compiler?
Re: Building TheIDE with using CMake [message #32313 is a reply to message #32312] Sat, 07 May 2011 21:09 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
Mindtraveller wrote on Sat, 07 May 2011 20:50

So you managed to build TheIDE with Digital Mars compiler?


Not tested for Digital Mars compiler (and didn't see it on CMake generators list).
Tested for GCC and MSC9 compilers only. Which was result from directly rewriting *.upp package files to CMakeLists.txt files.

May be, you can use it as reference.
Re: Building TheIDE with using CMake [message #32316 is a reply to message #32313] Sun, 08 May 2011 08:46 Go to previous messageGo to next message
chickenk is currently offline  chickenk
Messages: 168
Registered: May 2007
Location: Grenoble, France
Experienced Member
Nice to see a new effort in that direction Smile

When I have more time I'll have a look at your CMakefiles, I may find interesting ideas in it for my upp-waf build system.

You may also find some stuff in it, we never know...

Unfortunately I didn't have the opportunity to test it under windows (and I'm 100% sure it doesn't work there Wink) so congrats for the cross-platform portability!

Lionel
Re: Building TheIDE with using CMake [message #32317 is a reply to message #32316] Sun, 08 May 2011 15:03 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
Hello, Lionel.
Thanks for kind words.

I looked at your Waf build script. It's kind of "universal" build system for U++ packages.

The purposes of this thread is a bit different. It shows the possibility to create and build (using various native generators) complex U++ application(s), with using CMake.

Currently, one of the problems with *.icpp files solved directly, by renaming them to regular *.cpp files and building them alongside with "main" sources of application. It have some drawback, - additional step before building - and advantage, - correctly builds inside other IDEs, supported by CMake, without additional custom build steps (which also may be the possibility, otherwise).

Also, it possible to "globbing" C/C++ files to create list of sources to build, using aux_source_directory CMake command. But it is not used, - full sources provided in the list.

The "general" solution, for using with different U++ applications, can be some kind of CMake module(s), which resolves possible U++ build flags (e.g. flagGCC, flagMSC, - for compilers; flagBSD, flagFREEBSD, flagLINUX, flagOSX11, flagPOSIX - for platforms/OS, etc.) and checks include/linker directories. Now, it just have some CMake equivalents.

Honestly to say, I created CMakeLists.txt files for all current uppsrc packages, which gives me some kind of global review of used build flags/libraries, etc. But much of them, is kind of
set(SRC_LIST
	# C/C++ files
)

add_library(PackageName ${SRC_LIST})

set(USES_LIST) # dependent packages to link
set(LINK_LIST) # libraries to link

if(DEFINED USES_LIST)
	add_dependencies(PackageName ${USES_LIST})
endif(DEFINED USES_LIST)

if(DEFINED USES_LIST OR DEFINED LINK_LIST)
	target_link_libraries(PackageName ${USES_LIST} ${LINK_LIST})
endif(DEFINED USES_LIST OR DEFINED LINK_LIST)


Most of the "Core" packages is already there (Core, CtrlCore, CtrlLib, Draw, etc. packages).

Edit (08.05.2011): Added UppToCMakeLists.cmake script to attachments for partial conversion of U++ package to CMakeLists.txt file.
Edit: Fixed flag name for FreeBSD (flagFREEBSD).

[Updated on: Sat, 14 February 2015 13:02]

Report message to a moderator

Re: Building TheIDE with using CMake [message #34427 is a reply to message #32310] Tue, 22 November 2011 02:38 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
The first topic updated to 4179 release version.

The main changes are:
- Various definition checks for build flags and helper functions inside cmake/modules directory.
- Global initialization list, which fills with renamed *.icpp files inside concrete packages, instead of adding them to main package explicitly. The main package just get such global initialization list for sources list.
- Rainbow support for CMakeLists.txt files.
- The RenameFiles.cmake script added to archive(s).

[Updated on: Tue, 22 November 2011 04:31]

Report message to a moderator

Building umk with using CMake [message #34429 is a reply to message #32310] Tue, 22 November 2011 04:05 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
The umk is another useful command-line application to build U++ packages.

In the attachment you could find full umk source files with CMakeLists.txt files to build.

Edit: Updated to 4353 revision.

[Updated on: Thu, 29 December 2011 06:37]

Report message to a moderator

Re: Building TheIDE with using CMake [message #34463 is a reply to message #32310] Wed, 23 November 2011 15:23 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
In this message I will explain possible reason why renaming of *.icpp files was needed.

According to ""init" files in packages" topic, it is possible to use init file inside particular package to resolve *.icpp files dependencies. But practically, they are useful for command-line builds only. Mainly, because of lack of *.cpp file extension (e.g. init.cpp), which prevents using it inside another IDEs.

I made CMake scripts for three cases to demonstrate this problem:
1. Using init file of package directly by setting source properties as for CXX compiler and linker.
2. Using init.cpp file inside current package as an copy of init file. Including init.cpp file to source files to compile.
3. Using init.cpp file inside package binary directory with #include path to package init file. Including init.cpp file to source files to compile.

The CMakeLists.txt and build script files for mentioned cases you could find in the attachment.
To use, just create a backup of uppsrc directory (in case of testing) and copy files from particular case directory. Renaming of *.icpp files not needed.

To note:
The 2 and 3 cases are workable solutions for IDEs and command-line builds.

Edit: Fixed URL link.

[Updated on: Fri, 13 February 2015 19:02]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44254 is a reply to message #32310] Fri, 13 February 2015 13:16 Go to previous messageGo to next message
cyrion is currently offline  cyrion
Messages: 15
Registered: May 2006
Location: Grenoble, France
Promising Member
Hi!

I'm trying to use CMake to have QtCreator as my main UPP IDE.

Compilation & link is ok, but at runtime the GUI theme doesn't seems to apply.
I attached what I get for SQLApp: the window background is darker and popup menu background is totally black..

http://img11.hostingpics.net/thumbs/mini_411744cmaketheideprocmon.jpg

When I strace the application with processmonitor, I see that the one compiled with theIDE seems to look for the windows themes (right part of the image): there are a bunch of lines like:
CreateFile;C:\Windows\Resources\Themes\aero\aero.msstyles etc.)
QueryBasicInformationFile;C:\Windows\Resources\Themes\aero\aero.msstyles

that are not present in the version I compiled with CMake.

I also try forcing the theme with baazaar/Theme: it changes some colors but its not working.

I'm using the lastest nightly build of UPP.

Any hints of what I need to do to fix that?

Thx!
Damien.

[Updated on: Fri, 13 February 2015 18:57]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44256 is a reply to message #44254] Fri, 13 February 2015 14:37 Go to previous messageGo to next message
dolik.rce is currently offline  dolik.rce
Messages: 1778
Registered: August 2008
Location: Czech Republic
Senior Contributor

cyrion wrote on Fri, 13 February 2015 13:16
Hi!

I'm trying to use CMake to have QtCreator as my main UPP IDE.

Compilation & link is ok, but at runtime the GUI theme doesn't seems to apply.
I attached what I get for SQLApp: the window background is darker and popup menu background is totally black..

Hi Cyrion,

I can't tell you how to fix it, because I don't know. What I can tell you, is the reason: It is because of the *.icpp files (or those with this extension before renaming, as described above) not being linked correctly. They must be linked directly into the final executable, otherwise some of the registration code is removed as unused, while it is actually needed. You'll need to check what exactly happens in the build process and how those files are linked.

Best regards,
Honza
Re: Building TheIDE with using CMake [message #44257 is a reply to message #44254] Fri, 13 February 2015 17:43 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
Hello, Damien.

Your screenshot is quite big (wide, actually). I think, better to create small public image with URL to bigger private
image, but Bazaar attachments restricted to 1 attachment at the moment, therefore this might require to use external
source for this.

This topic was created in 2011 year (which is about 4 years ago). The proposed examples and CMakeLists.txt files wasn't
created automatically at the time of creation. Currently, they adapted to 4179 revision only (and some others with the same
file lists and dependencies).

What you could use to fix the issue in your screenshot is properly using *.icpp/init files, e.g. what explained in the above message.
You could find the related functions inside of uppsrc/cmake/modules directories of attachment, e.g. add_init_file function.

The automatic creation of CMakeLists.txt files might require to create correspondence between U++ and CMake defines,
methods to resolve library dependencies (or by using them directly as U++ TheIDE does). Also, there might be a binary files
used inside of U++ packages, which could require to write special parser for them.

Currently, this topic just show, that this is possible (manually).

[Updated on: Fri, 13 February 2015 21:09]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44258 is a reply to message #44256] Fri, 13 February 2015 18:26 Go to previous messageGo to next message
cyrion is currently offline  cyrion
Messages: 15
Registered: May 2006
Location: Grenoble, France
Promising Member
Thank you Honza, I miss that in the previous post.

About the icpp files, what I have done is simply create a new file like that:
For example SqlCtrl_init.icpp, I create a new file named SqlCtrl_init.icpp.cpp with the content:
#include "SqlCtrl_init.icpp"

That way, I did not mess UPP sources.

But I didn't notice I have to "force" linking to the objs. I'll try that!

Btw, I have a lot of warnings like the following:
warning LNK4221: This object file does not define any previously undefined public symbols, so it will not be used by any link operation that consumes this library

Not sure if its related..

[Updated on: Fri, 13 February 2015 18:35]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44259 is a reply to message #44257] Fri, 13 February 2015 18:34 Go to previous messageGo to next message
cyrion is currently offline  cyrion
Messages: 15
Registered: May 2006
Location: Grenoble, France
Promising Member
Hello Sender Ghost Smile
Sorry for the big image! Maybe I could have shrinked it, or yes, put it on some external server. I edited my post to put in on hostingpics.net.
In fact I merged my three screenshots into one big image because of the "one attachement" restriction.

About the CMakeFiles, I created them manually, I didn't used the one provided in this thread.
I had a quick look - way too quick actually - at the cmake modules: I'll check that too, I think that is the solution!

Thank you very much!

[Updated on: Fri, 13 February 2015 18:47]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44260 is a reply to message #32310] Fri, 13 February 2015 18:55 Go to previous messageGo to next message
cyrion is currently offline  cyrion
Messages: 15
Registered: May 2006
Location: Grenoble, France
Promising Member
Yeah, it's working Smile

Quite ugly, but I manually added the icpp files to the application target to make it sure they are linked with it, and everything ok!

include_directories( . )

set( sources
	SQLApp.h
	SQLApp.sch
	query.cpp
	book.cpp
	borrow.cpp
	main.cpp
        Theme.cpp
        Theme.h
        C:/upp/uppsrc/CodeEditor/CRegister.icpp.cpp
        C:/upp/uppsrc/Core/Core_init.icpp.cpp
        C:/upp/uppsrc/CtrlCore/CtrlCore.icpp.cpp
        C:/upp/uppsrc/CtrlLib/CtrlLib.icpp.cpp
        C:/upp/uppsrc/plugin/bmp/BmpReg.icpp.cpp
        C:/upp/uppsrc/plugin/png/pngreg.icpp.cpp
        C:/upp/uppsrc/Report/ReportI.icpp.cpp
        C:/upp/uppsrc/RichText/RichImage.icpp.cpp
        C:/upp/uppsrc/SqlCtrl/SqlCtrl_init.icpp.cpp
)

add_executable( SQLApp WIN32 ${sources} )




Now I just have to make something automatic & clean Smile I'm checking the previous posts...

Thank you very much all!
Re: Building TheIDE with using CMake [message #44261 is a reply to message #44260] Fri, 13 February 2015 19:23 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
cyrion wrote on Fri, 13 February 2015 17:55
Yeah, it's working :)
Quite ugly, but I manually added the icpp files to the application target to make it sure they are linked with it, and everything ok!

I updated the link to "init" files in packages topic for above message.

What you did with *.icpp files are created automatically by current TheIDE inside of init U++ package files.

For example, I wrote following add_init_function, from case 3:
function(add_init_file init_file copied_file)
	# Creates ${init_file}.cpp inside binary directory with #include path to ${init_file}
	# and returns the path to copied_file
	set(input_file ${CMAKE_CURRENT_SOURCE_DIR}/${init_file})
	if(EXISTS "${input_file}")
		set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${init_file}.cpp")
		file(WRITE "${output_file}" "#include \"${input_file}\"\n")
		set(${copied_file} "${output_file}" PARENT_SCOPE)
	endif()
endfunction()


For example, inside of CtrlCore CMakeLists.txt file:
# Header files
set(H_LIST
)
# Compilable files
set(SRC_LIST
)
# The *.icpp files. Just to edit/view them inside of IDEs
set(ISRC_LIST
	CtrlCore.icpp
)

set_source_files_properties(${H_LIST} ${ISRC_LIST} PROPERTIES HEADER_FILE_ONLY ON)

add_init_file(init INIT_FILE)
add_library(CtrlCore ${INIT_FILE} ${SRC_LIST} ${H_LIST} ${ISRC_LIST})

# And so on

[Updated on: Fri, 13 February 2015 19:24]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44262 is a reply to message #44261] Fri, 13 February 2015 19:30 Go to previous messageGo to next message
cyrion is currently offline  cyrion
Messages: 15
Registered: May 2006
Location: Grenoble, France
Promising Member
Yep, I saw that, I'm on it!

Your script is working, and creates the ini files in the bin dir, but they are not linked with the main app exe, so it's not working directly for me here.
Also, since it's constantly updating the init.cpp files, CMake recompile them all the time. Annoying... Smile

So what I'm trying to do currently is creating a new target with all the init.cpp, or maybe a global variable holding all the files, in order to add them (or the target) to the dependencies of the executable..

[EDIT] Btw I don't receive notifications of replies in the thread.. I unsubscribed/subscribed but nothing..

[Updated on: Fri, 13 February 2015 19:33]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44263 is a reply to message #44262] Fri, 13 February 2015 19:46 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
cyrion wrote on Fri, 13 February 2015 18:30
Your script is working, and creates the ini files in the bin dir, but they are not linked with the main app exe

This is wrong assumption. What I wrote in message 44261 is excerpt from actual attachment.

cyrion wrote on Fri, 13 February 2015 18:30
Also, since it's constantly updating the init.cpp files, CMake recompile them all the time.

The method was created for IDEs, mainly to configure CMake files once, which allows to view/edit and build U++ package files, when needed. I didn't have issue, which you mentioned.

No need to create competition here. This topic was created for different purpose (e.g. to show the possibility in 2011 year). The semi-automatic method already exists. There are other solutions also, which not use CMake.
Re: Building TheIDE with using CMake [message #44264 is a reply to message #44262] Fri, 13 February 2015 21:21 Go to previous messageGo to next message
Sender Ghost is currently offline  Sender Ghost
Messages: 301
Registered: November 2008
Senior Member
What I said might look like as rudeness, but it is not.

There were many CMake releases (and possible compatibility issues for previous versions) from 2011 year.
Constantly updating the attachments for new versions of U++ and CMake is not productive, in my opinion, especially when done manually.

This topic is open for questions, related to the topic messages. But they might be quite outdated, when used with modern U++ and CMake.

If you have a (new) method or a way how to use CMake and U++, then better to create a new topic about this, in my opinion.

Overall, I glad that somebody is interested in what said and done in this topic (in broad sense of this word).

[Updated on: Fri, 13 February 2015 21:59]

Report message to a moderator

Re: Building TheIDE with using CMake [message #44265 is a reply to message #44264] Sat, 14 February 2015 01:05 Go to previous messageGo to next message
cyrion is currently offline  cyrion
Messages: 15
Registered: May 2006
Location: Grenoble, France
Promising Member
Quote:

What I said might look like as rudeness, but it is not.

No problem, I was not there for a few hours, I just saw your answer now.
Thank you for the updated links!

Quote:

There were many CMake releases (and possible compatibility issues for previous versions) from 2011 year.
Constantly updating the attachments for new versions of U++ and CMake is not productive, in my opinion, especially when done manually.

This topic is open for questions, related to the topic messages. But they might be quite outdated, when used with modern U++ and CMake.

If you have a (new) method or a way how to use CMake and U++, then better to create a new topic about this, in my opinion.

From my side, I'm just happy I found a solution to my problem with your help!
In fact I started creating my CMakeFiles without knowing you already explored that problem, then I found this forum entry when I looked for a solution to my colors bug.
Thought it was the good place to ask - and in fact it was Smile Thanks again!

I really love the UPP way to create nice and small GUI apps without external dependencies, and I just wanted to make this working with QtCreator.
TheIDE is great, fast & smooth ; I like the nested code blocks with the different colors, the << completion and the indispensable statistics and elapsed time Smile but IMHO it lacks some recent functionalities I'm really used to: easy global/local search & replace, simultaneous multiple lines editing (aka "column mode" or "block selection mode"), refactoring, ctrk/k+key stuff, VIM integration, etc. Also JOM for parallel builds is probably as fast as BLITZ at first sight, maybe faster. I'll check that but I first have to add the PCH to be fair Smile

So my goal here was not to automatically create CMakeFiles from upp projects, I'm good with the idea of maintaining my CMakeFiles manually, but of course that's only a workaround.

About my final solution - no competition here Wink - I updated your function add_init_file for this one:
function(create_cpps_from_icpps )
    file( GLOB icpp_files RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*.icpp"   )
    foreach( icppFile ${icpp_files} )
        set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${icppFile}.cpp")
        file(WRITE "${output_file}" "#include \"${CMAKE_CURRENT_SOURCE_DIR}/${icppFile}\"\n")
    endforeach()
endfunction()

It creates the icpp.cpp files in the binary dir. Each one of them having the name of the original icpp it makes easier to catch compile / link errors compared to the multiple init.cpp that had the same name.

So as you may have noticed, I do not rely on Mirek ini files. That's probably not a good idea, but when I tried using them it added a lot of unnecessary (in my case) dependencies, missing symbols and also multiple symbols definitions that broke everything.
I didn't really digged very far to check why, but I think ini files can potentially add the same cpp multiple times.
The way I do makes possible to only link with the required modules, and not the others.

To finish, my main app looks like that :
set( sources
	SQLApp.h
	SQLApp.sch
	query.cpp
	book.cpp
	borrow.cpp
	main.cpp
)

file( GLOB_RECURSE ini_files "${CMAKE_CURRENT_BINARY_DIR}/../*.icpp.cpp"   )

add_executable( SQLApp WIN32 ${sources} ${ini_files} )

The GLOB_RECURSE stuff is not very pretty, but it does the job: it links the icpp to the app the exact same way that fixed my problem at first.

For now its working, but I only use a subset of UPP, namely :
add_subdirectory( "plugin/z"  )
add_subdirectory( Core )
add_subdirectory( Draw )
add_subdirectory( "plugin/bmp"  )
add_subdirectory( "plugin/png"  )
add_subdirectory( RichText )
add_subdirectory( CtrlCore )
add_subdirectory( CtrlLib  )
add_subdirectory( "plugin/sqlite3"  )
add_subdirectory( Sql  )
add_subdirectory( SqlCtrl  )
add_subdirectory( Report  )
add_subdirectory( CodeEditor  )
add_subdirectory( "plugin/pcre" )
add_subdirectory( "plugin/zip" )

So maybe it will break in the future, I don't know!


[Updated on: Sat, 14 February 2015 01:16]

Report message to a moderator

Re: Building TheIDE with using CMake [message #46556 is a reply to message #44265] Thu, 26 May 2016 08:16 Go to previous messageGo to previous message
coolman is currently offline  coolman
Messages: 58
Registered: April 2006
Location: Czech Republic
Member
Hi,

Based on this discussion I created bash script to generate CMakeList.txt from the Ultimate++ project - see https://github.com/CoolmanCZ/upp_cmake

Script was tested on Ubuntu 14.04LTS with GCC 4.9+. Any comments are welcome.

Radek

[Updated on: Thu, 26 May 2016 08:22]

Report message to a moderator

Previous Topic: found a solution in the interim - firebird
Next Topic: Am I doing something wrong, or is there a bug? FIREBIRD
Goto Forum:
  


Current Time: Thu Jun 04 05:35:22 CEST 2020

Total time taken to generate the page: 0.03896 seconds