diff options
33 files changed, 5928 insertions, 340 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2921af0..dfd0a38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -493,6 +493,7 @@ include_directories(${Boost_INCLUDE_DIR}) find_package(EVENT REQUIRED) include_directories(${EVENT_INCLUDE_DIR}) list (APPEND USCXML_CORE_LIBS ${EVENT_LIBRARY}) +#set(EVENT_SSL_FOUND OFF) # deactivate for now ################################################# # Optional libraries @@ -527,6 +528,13 @@ else() endif() +find_package(OpenSSL) +if (OPENSSL_FOUND) + include_directories(${OPENSSL_INCLUDE_DIR}) + list (APPEND USCXML_OPT_LIBS ${OPENSSL_LIBRARIES}) +endif() + + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SHARED}) find_package(SWI) if (SWI_FOUND) @@ -729,6 +737,12 @@ foreach( FILE ${USCXML_FILES} ) endif() endforeach() +# add compile time reducer +# see https://github.com/sakra/cotire +if ((WIN32 OR UNIX) AND NOT APPLE) + include(cotire) +endif() + # build library if (BUILD_AS_PLUGINS) add_library(uscxml ${USCXML_FILES}) @@ -737,6 +751,10 @@ else() add_library(uscxml ${USCXML_FILES}) target_link_libraries(uscxml ${USCXML_OPT_LIBS} ${USCXML_CORE_LIBS}) endif() +if ((WIN32 OR UNIX) AND NOT APPLE) + set_target_properties(uscxml PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "src/uscxml/pch.h") + cotire(uscxml) +endif() if (NOT CMAKE_CROSSCOMPILING) add_executable(uscxml-browser apps/uscxml-browser.cpp ${PROJECT_SOURCE_DIR}/contrib/src/getopt/XGetopt.cpp) diff --git a/apps/samples/map/spoken-map-ticker.scxml b/apps/samples/map/spoken-map-ticker.scxml index 147f890..3955715 100644 --- a/apps/samples/map/spoken-map-ticker.scxml +++ b/apps/samples/map/spoken-map-ticker.scxml @@ -27,7 +27,7 @@ <invoke type="umundo" id="umundo"> <param name="channel" expr="'map/tick'" /> <finalize> - <script>dump(_event);</script> + <!-- <script>dump(_event);</script> --> </finalize> </invoke> diff --git a/apps/samples/miles/miles.js b/apps/samples/miles/miles.js new file mode 100644 index 0000000..a3cdcae --- /dev/null +++ b/apps/samples/miles/miles.js @@ -0,0 +1,664 @@ +function Miles(element, params) { + + // private attributes + var self = this; + + // private instanceId + if (!Miles.instances) + Miles.instances = 0; + this.instanceId = Miles.instances++; + + // public attributes + this.reflectorURL = "127.0.0.1"; + this.params = params; + + // privileged public methods + this.foo = function() { + }; + + // privileged private methods + var bar = function(param1, param2) { + }; + + // start of actual class after we fetched all dojo thingies + require(["dojo/dom-construct", + "dojo/_base/xhr", + "dojo/dom", + "dojo/on", + "dojox/storage", + "dojo/store/Memory", + "dojo/store/Observable", + "dijit/tree/ObjectStoreModel", + "dijit/Tree", + "dijit/form/TextBox", + "dijit/form/Button", + "dojox/widget/Standby", + "dijit/form/DropDownButton", + "dijit/TooltipDialog", + "dojo/dnd/Moveable", + "dojo/ready", + "dojo/dnd/Source", + "dijit/form/HorizontalSlider", + "dijit/form/Select", + "dijit/form/NumberSpinner"], + function(domConst, + xhr, + dom, + on, + storage, + Memory, + Observable, + ObjectStoreModel, + Tree, + TextBox, + Button, + Standby, + DropDownButton, + TooltipDialog, + Moveable, + ready, + Source, + HorizontalSlider, + Selector, + NumberSpinner) { + + ready(function() { + + if (typeof(element) === 'string') { + element = dom.byId(element); + } + + self.element = element; + self.xhr = xhr; + self.localStorage = dojox.storage.manager.getProvider(); + self.localStorage.initialize(); + + // establish our dom + element.appendChild(domConst.toDom('\ + <table>\ + <tr>\ + <td valign="top">\ + <div style="position: relative; padding: 0px">\ + <img class="model" src="' + self.resRoot + 'img/Tutorial.png" style="z-index: -1; width: ' + self.pose.width + 'px; height: ' + self.pose.height + 'px"></img>\ + <div style="z-index: 1; position: absolute; right: 45%; top: 45%">\ + <div class="progress"></div>\ + </div>\ + <div style="position: absolute; left: 10px; top: 10px">\ + <table></tr>\ + <td class="filesDropDown" style="vertical-align: middle"></td>\ + <td>\ + <div class="movieDropDown" style="display: inline"></div>\ + <button type="button" class="movieAddButton"></button>\ + </td>\ + <td align="right"><button type="button" class="resetButton"></button></td>\ + <td class="dragHandler" style="vertical-align: middle; padding-top: 4px;"></td>\ + </tr></table>\ + </div>\ + <div style="position: absolute; right: 10px; top: 15%; height: 50%">\ + <div class="zoomSlide"></div>\ + </div>\ + <div style="position: absolute; right: 50%; top: 50%">\ + <div class="pitchRollHandler" style="font-size: 0.5em; background-color: rgba(255,255,255,0.5); border-radius: 5px; moz-border-radius: 5px;">\ + <table>\ + <tr>\ + <td><img class="pitchRollHandlerImg" src="' + self.resRoot + 'img/pitchRoll-handle.png" height="20px" style="padding: 2px 0px 0px 4px;" /></td>\ + <td><div class="pitchLabel"></div><div class="rollLabel"></div></td>\ + </tr>\ + </table>\ + </div>\ + </div>\ + <div style="position: absolute; right: 50%; top: 50%">\ + <div class="yawZoomHandler">\ + <div style="font-size: 0.5em; background-color: rgba(255,255,255,0.5); border-radius: 5px; moz-border-radius: 5px; position: absolute; left: -34px;">\ + <table>\ + <tr>\ + <td><img class="yawZoomHandlerImg" src="' + self.resRoot + 'img/yawZoom-handle.png" height="20px" style="padding: 2px 0px 0px 4px;" /></td>\ + <td><div class="yawLabel"></div><div class="zoomLabel"></div></td>\ + </tr>\ + </table>\ + </div>\ + </div>\ + </div>\ + <div style="position: absolute; right: 50%; top: 50%">\ + <div class="xyHandler" style="font-size: 0.5em; background-color: rgba(255,255,255,0.5); border-radius: 5px; moz-border-radius: 5px;">\ + <table>\ + <tr>\ + <td><img class="xyHandlerImg" src="' + self.resRoot + 'img/xy-handle.png" width="20px" style="padding: 2px 0px 0px 4px;" /></td>\ + <td><div class="xLabel"></div><div class="yLabel"></div></td>\ + </tr>\ + </table>\ + </div>\ + </div>\ + </div>\ + </td>\ + <td valign="top" height="100%">\ + </td>\ + </tr>\ + <tr>\ + <td colspan="2"><div class="messages"></div></td>\ + </tr>\ + </table>\ + ')); + + // fetch special dom nodes for content + self.messageBox = dojo.query("div.messages", element)[0]; + self.imgElem = dojo.query("img.model", element)[0]; + + /** + * === POSE MANIPULATION AND RESET ==================== + */ + + self.resetButtonElem = dojo.query("button.resetButton", element)[0]; + self.progressElem = dojo.query("div.progress", element)[0]; + self.pitchRollHandlerElem = dojo.query(".pitchRollHandler", element)[0]; + self.yawZoomHandlerElem = dojo.query(".yawZoomHandler", element)[0]; + self.xyHandlerElem = dojo.query(".xyHandler", element)[0]; + + self.pitchRollHandler = new Moveable(self.pitchRollHandlerElem); + self.pitchRollHandler.onMoveStop = function(mover) { + var handlerImg = dojo.query("img.pitchRollHandlerImg", mover.node)[0]; + var pitchLabel = dojo.query("div.pitchLabel", mover.node)[0]; + var rollLabel = dojo.query("div.rollLabel", mover.node)[0]; + + pitchLabel.innerHTML = ''; + rollLabel.innerHTML = ''; + + self.updateScene(); + }; + self.pitchRollHandler.onMoving = function(mover) { + // mover.node.style.backgroundColor = "rgba(255,255,255,0.5)"; + // mover.node.style.borderRadius = "5px"; + // mover.node.style.mozBorderRadius = "5px"; + // mover.node.style.webkitBorderRadius = "5px"; + var handlerImg = dojo.query(".pitchRollHandlerImg", mover.node)[0]; + var pitchLabel = dojo.query(".pitchLabel", mover.node)[0]; + var rollLabel = dojo.query(".rollLabel", mover.node)[0]; + var offset = moverRelativeTo(handlerImg, self.element); + + offset.x += 30; + offset.y += 20; + + self.xyHandlerElem.style.zIndex = 1; + self.yawZoomHandlerElem.style.zIndex = 1; + self.pitchRollHandlerElem.style.zIndex = 2; + + // self.pose.pitch = self.pose.pitch % (2 * 3.14159); + // self.pose.roll = self.pose.roll % (2 * 3.14159); + self.pose.roll = offset.x / self.pose.width - 0.5; + self.pose.pitch = offset.y / self.pose.height - 0.5; + self.pose.pitch *= -1; + self.pose.roll *= 2 * 3.14159; + self.pose.pitch *= 2 * 3.14159; + self.pose.roll = Math.ceil((self.pose.roll) * 10) / 10; + self.pose.pitch = Math.ceil((self.pose.pitch) * 10) / 10; + pitchLabel.innerHTML = 'Pitch:' + self.pose.pitch; + rollLabel.innerHTML = 'Roll:' + self.pose.roll; + }; + + self.yawZoomHandler = new Moveable(self.yawZoomHandlerElem); + self.yawZoomHandler.onMoveStop = function(mover) { + var handlerImg = dojo.query("img.yawZoomHandlerImg", mover.node)[0]; + var yawLabel = dojo.query("div.yawLabel", mover.node)[0]; + var zoomLabel = dojo.query("div.zoomLabel", mover.node)[0]; + + yawLabel.innerHTML = ''; + zoomLabel.innerHTML = ''; + + self.updateScene(); + }; + self.yawZoomHandler.onMoving = function(mover) { + var handlerImg = dojo.query("img.yawZoomHandlerImg", mover.node)[0]; + var yawLabel = dojo.query("div.yawLabel", mover.node)[0]; + var zoomLabel = dojo.query("div.zoomLabel", mover.node)[0]; + var offset = moverRelativeTo(handlerImg, self.element); + + offset.x += 7; + offset.y += 9; + + self.xyHandlerElem.style.zIndex = 1; + self.yawZoomHandlerElem.style.zIndex = 2; + self.pitchRollHandlerElem.style.zIndex = 1; + + // self.pose.pitch = self.pose.pitch % (2 * 3.14159); + // self.pose.roll = self.pose.roll % (2 * 3.14159); + self.pose.yaw = (self.pose.width - offset.x) / self.pose.width - 0.5; + self.pose.zoom = offset.y / self.pose.height - 0.5; + self.pose.yaw *= 2 * 3.14159; + self.pose.zoom = self.pose.zoom * 3 + 1; + self.pose.zoom = Math.ceil((self.pose.zoom) * 10) / 10; + self.pose.yaw = Math.ceil((self.pose.yaw) * 10) / 10; + yawLabel.innerHTML = 'Yaw:' + self.pose.yaw; + zoomLabel.innerHTML = 'Zoom:' + self.pose.zoom; + }; + + self.xyHandler = new Moveable(self.xyHandlerElem); + self.xyHandler.onMoveStop = function(mover) { + var handlerImg = dojo.query("img.xyHandlerImg", mover.node)[0]; + var xLabel = dojo.query("div.xLabel", mover.node)[0]; + var yLabel = dojo.query("div.yLabel", mover.node)[0]; + + xLabel.innerHTML = ''; + yLabel.innerHTML = ''; + + self.updateScene(); + }; + self.xyHandler.onMoving = function(mover) { + var handlerImg = dojo.query("img.xyHandlerImg", mover.node)[0]; + var xLabel = dojo.query("div.xLabel", mover.node)[0]; + var yLabel = dojo.query("div.yLabel", mover.node)[0]; + var offset = moverRelativeTo(handlerImg, self.element); + + offset.x += 3; + offset.y += 13; + + self.xyHandlerElem.style.zIndex = 2; + self.yawZoomHandlerElem.style.zIndex = 1; + self.pitchRollHandlerElem.style.zIndex = 1; + + self.pose.x = offset.x / self.pose.width - 0.5; + self.pose.y = offset.y / self.pose.height - 0.5; + self.pose.x *= 100; + self.pose.y *= 100; + self.pose.y = Math.ceil((self.pose.y) * 10) / 10; + self.pose.x = Math.ceil((self.pose.x) * 10) / 10; + xLabel.innerHTML = 'X:' + self.pose.x; + yLabel.innerHTML = 'Y:' + self.pose.y; + }; + + /** + * === FILES DROPDOWN ==================== + */ + + self.filesDropDownElem = dojo.query("td.filesDropDown", element)[0]; + + self.createAvatar = function(item, mode) { + if (mode == 'avatar') { + // create your avatar if you want + var avatar = dojo.create( 'div', { innerHTML: item.data }); + var avatarPose = dojo.clone(self.pose); + avatarPose.width=60; + avatarPose.height=60; + var avatarImgUrl = urlSuffixForPose(avatarPose); + avatar.innerHTML = '<img src=' + self.imageURL + avatarImgUrl + ' /> '; + item.srcEcc = "VRMLViewer"; + item.iconPoseUrl = self.imageURL + avatarImgUrl; + item.imageURL = self.imageURL; + item.serverURL = self.serverURL; + item.pose = avatarPose; + return {node: avatar, data: item, type: item.type}; + } + var handler = dojo.create( 'div', { innerHTML: '<img src="' + self.resRoot + 'img/drag.png" width="20px" />' }); + return {node: handler, data: item, type: item.type}; + }; + + self.dragHandler = new Source(dojo.query("td.dragHandler", element)[0], {copyOnly: true, creator: self.createAvatar}); + self.dragHandler.insertNodes(false, [ { } ]); + + // setup fileStore for tree list + self.fileStore = new Observable(new Memory({ + data: [ { id: 'root', name:'3D Models'} ], + getChildren: function(object){ + return this.query({parent: object.id}); + } + })); + self.fileTreeModel = new ObjectStoreModel({ + store: self.fileStore, + query: { id: "root" } + }); + + // setup actual tree dijit + self.fileList = new dijit.Tree({ + id: "fileList" + self.instanceId, + model: self.fileTreeModel, + persist: false, + showRoot: false, + style: "height: 300px;", + onClick: function(item){ + if ('url' in item) { + self.imageURL = item.url; + self.updateScene(); + } + }, + getIconClass: function(item, opened) { + return (!item || !('url' in item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"; + }, + getIconStyle: function(item, opened){ + if('url' in item) { + return { backgroundImage: "url('" + item.url + "?width=16&height=16')"}; + } + } + //return {backgroundImage: "url('" + item.url + "?width=16&height=16')"}; + + }); + + if (self.params && self.params.serverURL) + self.serverURL = self.params.serverURL; + var savedServerURL = self.localStorage.get("vrmlServer"); + if (savedServerURL && !self.serverURL) { + self.serverURL = savedServerURL; + self.refreshServer(savedServerURL); + } + + self.serverBox = new TextBox({ + name: "Server", + value: self.serverURL, + style: "width: 65%", + + onKeyUp: function(e) { + if (self.browseButton) { + if (this.get("value") !== self.serverURL) { + self.browseButton.setAttribute('label', 'Browse'); + } else { + self.browseButton.setAttribute('label', 'Refresh'); + } + } + }, + + onKeyDown: function(e) { + var code = e.keyCode || e.which; + if( code === 13 ) { + e.preventDefault(); + self.refreshServer(this.get("value")); + return false; + } + }, + }); + + self.browseButton = new Button({ + label: "Browse", + onClick: function(){ + self.refreshServer(self.serverBox.get("value")); + } + }); + + self.filesDropDownContent = domConst.toDom('<div />'); + self.filesDropDownContent.appendChild(self.serverBox.domNode); + self.filesDropDownContent.appendChild(self.browseButton.domNode); + self.filesDropDownContent.appendChild(self.fileList.domNode); + + self.filesToolTip = new TooltipDialog({ content:self.filesDropDownContent, style:"max-height:320px"}); + self.filesDropDown = new DropDownButton({ label: "Files", dropDown: self.filesToolTip }); + self.filesDropDownElem.appendChild(self.filesDropDown.domNode); + + self.fileStandby = new Standby({target: self.filesDropDownContent }); + self.filesDropDownContent.appendChild(self.fileStandby.domNode); + + /** + * === MOVIE DROPDOWN ==================== + */ + + self.movieDropDownElem = dojo.query("div.movieDropDown", element)[0]; + self.movieAddButtonElem = dojo.query("button.movieAddButton", element)[0]; + + self.movieDropDownContent = domConst.toDom( + '<div style="overflow: auto; max-height: 420px;"> \ + <table><tr class="movieFormatLengthRow" /></tr><tr class="movieWidthHeightLengthRow" /></table> \ + <div class=\"dndArea\" /> \ + </div>' + ); + + self.movieFormatLengthRowElem = dojo.query("tr.movieFormatLengthRow", self.movieDropDownContent)[0]; + self.movieWidthHeightLengthRowElem = dojo.query("tr.movieWidthHeightLengthRow", self.movieDropDownContent)[0]; + self.movieDnDArea = dojo.query("div.dndArea", self.movieDropDownContent)[0]; + + self.createMovieThumb = function(item, mode) { + if (mode == 'avatar') { + // when dragged + var avatar = dojo.create( 'div', { innerHTML: item.data }); + var avatarPose = dojo.clone(self.pose); + avatarPose.width = 60; + avatarPose.height = 60; + var avatarImgUrl = urlSuffixForPose(avatarPose); + avatar.innerHTML = '<img src=' + self.imageURL + avatarImgUrl + ' /> '; + item.srcEcc = "VRMLViewer"; + item.iconPoseUrl = self.imageURL + avatarImgUrl; + item.imageURL = self.imageURL; + item.serverURL = self.serverURL; + item.pose = avatarPose; + return {node: avatar, data: item, type: item.type}; + } else { + + // when added to list + var thumb = domConst.toDom("\ + <div>\ + <table><tr><td>\ + <img class=\"movieThumb\"/>\ + <img class=\"removeThumb\" style=\"vertical-align: top; margin: -3px 0px 0px -8px; width: 20px; height: 20px;\"/>\ + </td><td align=\"left\">\ + <table><tr>\ + <td>Frame:</td><td><div class=\"relFrameLength\"/></td>\ + <td><div class=\"fillInSeries\" \></td>\ + </tr><tr>\ + <td>Transition:</td><td><div class=\"relTransitionLength\"/></td>\ + </tr></table>\ + </td></tr></table>\ + </div>\ + "); + thumb = dojo.query("div", thumb)[0]; + + var thumbImgElem = dojo.query("img.movieThumb", thumb)[0]; + var removeImgElem = dojo.query("img.removeThumb", thumb)[0]; + var relFrameLengthElem = dojo.query("div.relFrameLength", thumb)[0]; + var relTransitionLengthElem = dojo.query("div.relTransitionLength", thumb)[0]; + var fillInSeriesElem = dojo.query("div.fillInSeries", thumb)[0]; + + item.getThisAndNeighborsFromDnD = function() { + var thisAndNeighbors = {}; + self.addToMovieHandler.forInItems(function(obj, key, ctx) { + if (obj.data === item) { + thisAndNeighbors.this = { key: key, obj: obj }; + } else { + thisAndNeighbors.before = { key: key, obj: obj }; + } + if (thisAndNeighbors.this) { + thisAndNeighbors.after = { key: key, obj: obj }; + return thisAndNeighbors; + } + }); + return thisAndNeighbors; + }; + + item.relFrameLengthSlider = new HorizontalSlider({ + value: 50, + title: "Relative Duration of Frame", + style: "width:150px;" + }, relFrameLengthElem); + + item.relTransitionLengthSlider = new HorizontalSlider({ + value: 100, + title: "Relative Duration of Transition", + style: "width:150px;" + }, relTransitionLengthElem); + + removeImgElem.onclick = function() { + var thisItem = item.getThisAndNeighborsFromDnD(); + if (thisItem.this) { + // haha - what a mess! + self.addToMovieHandler.selectNone(); + self.addToMovieHandler.selection[thisItem.this.key] = thisItem.this.obj; + self.addToMovieHandler.deleteSelectedNodes(); + } + // disable create button if this was the last one + if (!thisItem.after || !thisItem.before) { + self.movieCreateButton.setAttribute('disabled', true); + } + } + + item.fillInSeriesButton = new Button({ + label: "Insert Series", + style: "display: none;", + onClick: function(){ + alert("foo"); + } + }, fillInSeriesElem); + + removeImgElem.src = self.resRoot + "img/close.png"; + + var thumbPose = dojo.clone(self.pose); + thumbPose.width = self.pose.width / 10; + thumbPose.height = self.pose.height / 10; + var thumbImgUrl = urlSuffixForPose(thumbPose); + + thumbImgElem.src = self.imageURL + thumbImgUrl; + // removeImgElem.src = self.resRoot + 'img/close.png'; + + item.srcEcc = "VRMLViewer"; + item.iconPoseUrl = self.imageURL + thumbImgUrl; + item.imageURL = self.imageURL; + item.serverURL = self.serverURL; + item.pose = thumbPose; + + return {node: thumb, data: item, type: item.type}; + } + }; + + self.addToMovieHandler = new Source(self.movieDnDArea, {copyOnly: true, creator: self.createMovieThumb}); + + self.movieFormatSelection = new Selector({ + name: "movieFormat", + style: "width: 320px", + options: [] + }); + self.populateMovieCodecs(self.serverURL + '/movie/codecs', self.movieFormatSelection); + + self.movieFormatLengthRowElem.appendChild(dojo.create('td', { innerHTML: 'Format:'} )); + self.movieFormatLengthRowElem.appendChild(dojo.create('td', { colspan: "2"})); + self.movieFormatLengthRowElem.lastChild.appendChild(self.movieFormatSelection.domNode); + + self.movieHeightSpinner = new NumberSpinner({ + value: 400, + smallDelta: 1, + style: "width: 60px", + constraints: { min:40, places:0 }, + }); + + self.movieWidthSpinner = new NumberSpinner({ + value: 600, + smallDelta: 1, + style: "width: 60px", + constraints: { min:40, places:0 }, + }); + + self.movieCreateButton = new Button({ + label: "Create", + disabled: true, + onClick: function(){ + + var form = document.createElement("form"); + + form.setAttribute("method", "post"); + form.setAttribute("action", self.serverURL + "/movie"); + + var submitData = {}; + submitData.frames = []; + submitData.movieLength = self.movieDurationSpinner.value; + submitData.format = self.movieFormatSelection.value; + submitData.width = self.movieWidthSpinner.value; + submitData.height = self.movieHeightSpinner.value; + + self.addToMovieHandler.forInItems(function(obj, key, ctx) { + var jsonData = { + iconPoseUrl: obj.data.iconPoseUrl, + imageURL: obj.data.imageURL, + serverURL: obj.data.serverURL, + pose: obj.data.pose, + relFrameLength: obj.data.relFrameLengthSlider.value, + relTransitionLength: obj.data.relTransitionLengthSlider.value, + } + submitData.frames.push(jsonData); + }); + + var hiddenField = document.createElement("input"); + hiddenField.setAttribute("type", "hidden"); + hiddenField.setAttribute("name", "data"); + hiddenField.setAttribute("value", JSON.stringify(submitData)); + + form.appendChild(hiddenField); + + // this will not save the returned binary file + // self.xhr.post({ + // form: form, + // load: function(data){ + // alert("asd"); + // } + // }); + + document.body.appendChild(form); + form.submit(); + document.body.removeChild(form); + } + }); + + self.movieDurationSpinner = new NumberSpinner({ + value: 10, + smallDelta: 1, + style: "width: 40px", + constraints: { min:0, places:0 }, + }); + + // append format duration cell + self.movieWidthHeightLengthRowElem.appendChild(dojo.create('td', { innerHTML: 'Size:'} )); + var movieDimensionCell = dojo.create('td'); + movieDimensionCell.appendChild(self.movieWidthSpinner.domNode); + movieDimensionCell.appendChild(dojo.create('span', { innerHTML: "x"} )); + movieDimensionCell.appendChild(self.movieHeightSpinner.domNode); + movieDimensionCell.appendChild(self.movieDurationSpinner.domNode); + movieDimensionCell.appendChild(dojo.create('span', { innerHTML: "sec"} )); + self.movieWidthHeightLengthRowElem.appendChild(movieDimensionCell); + + self.movieWidthHeightLengthRowElem.appendChild(dojo.create('td', { align: "right"})); + self.movieWidthHeightLengthRowElem.lastChild.appendChild(self.movieCreateButton.domNode); + + + self.movieToolTip = new TooltipDialog({ content:self.movieDropDownContent }); + self.movieDropDown = new DropDownButton({ + label: "Movie", + style: "display: none;", + dropDown: self.movieToolTip }); + self.movieDropDownElem.appendChild(self.movieDropDown.domNode); + + self.movieAddButton = new Button({ + label: "+", + style: "margin-left: -10px; display: none;", + onClick: function(){ + if (self.movieFormatSelection.options.length == 0) { + self.populateMovieCodecs(self.serverURL + '/movie/codecs', self.movieFormatSelection); + } + // we could pass item.data here to creator + self.addToMovieHandler.insertNodes(false, [ { } ]); + self.movieCreateButton.setAttribute('disabled', false); + + } + }, self.movieAddButtonElem); + + + self.resetButton = new Button({ + label: "Reset", + onClick: function(){ + self.pose.x = 0; + self.pose.y = 0; + self.pose.pitch = 0; + self.pose.roll = 0; + self.pose.yaw = 0; + self.pose.zoom = 1; + + self.xyHandler.node.style.left = 0; + self.xyHandler.node.style.top = 0; + self.pitchRollHandler.node.style.left = 0; + self.pitchRollHandler.node.style.top = 0; + self.yawZoomHandler.node.style.left = 0; + self.yawZoomHandler.node.style.top = 0; + + self.updateScene(); + } + }, self.resetButtonElem); + + // do we have parameters for the initial pose? + if(self.params && self.params.pose) + self.setPose(self.params.imageURL, self.params.pose, self.params.serverURL); + + }); + }); + + +} diff --git a/apps/samples/vrml/vrml-server.scxml b/apps/samples/vrml/vrml-server.scxml index 5ee28dd..2f5c4ca 100644 --- a/apps/samples/vrml/vrml-server.scxml +++ b/apps/samples/vrml/vrml-server.scxml @@ -179,7 +179,7 @@ <!-- Start the osgconvert invoker to transform 3D files --> <invoke type="osgconvert" id="osgvonvert.osgb"> - <param name="threads" expr="20" /> + <param name="threads" expr="4" /> <finalize> <script> //dump(_event); diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp index fa108f8..566f6bc 100644 --- a/apps/uscxml-browser.cpp +++ b/apps/uscxml-browser.cpp @@ -15,10 +15,6 @@ #include <dlfcn.h> #endif -#ifdef _WIN32 -#include "XGetopt.h" -#endif - class VerboseMonitor : public uscxml::InterpreterMonitor { void onStableConfiguration(uscxml::Interpreter interpreter) { printConfig(interpreter.getConfiguration()); @@ -108,23 +104,6 @@ void customTerminate() { } #endif -void printUsageAndExit() { - printf("uscxml-browser version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n"); - printf("Usage\n"); - printf("\tuscxml-browser"); -#ifdef BUILD_AS_PLUGINS - printf(" [-e pluginPath]"); -#endif - printf("[-v] [-pN] URL\n"); - printf("\n"); - printf("Options\n"); - printf("\t-v : be verbose\n"); - printf("\t-pN : port for HTTP server\n"); - printf("\t-d : write each configuration as a dot file\n"); - printf("\n"); - exit(1); -} - int main(int argc, char** argv) { using namespace uscxml; @@ -136,75 +115,78 @@ int main(int argc, char** argv) { std::set_terminate(customTerminate); #endif - if (argc < 2) { - printUsageAndExit(); + InterpreterOptions options = InterpreterOptions::fromCmdLine(argc, argv); + if (!options) { + InterpreterOptions::printUsageAndExit(argv[0]); } - - bool verbose = false; - bool useDot = false; - bool glogIsInitialized = false; - size_t port = 8080; + // setup logging google::LogToStderr(); - -#ifndef _WIN32 - opterr = 0; -#endif - int option; - while ((option = getopt(argc, argv, "dvl:e:p:")) != -1) { - switch(option) { - case 'l': - google::InitGoogleLogging(optarg); - glogIsInitialized = true; - break; - case 'e': - uscxml::Factory::pluginPath = optarg; - break; - case 'd': - useDot = true; - break; - case 'p': - port = strTo<size_t>(optarg); - break; - case 'v': - verbose = true; - break; - case '?': - break; - default: - printUsageAndExit(); - break; - } + google::InitGoogleLogging(argv[0]); + + // setup HTTP server + HTTPServer::SSLConfig* sslConf = NULL; + if (options.certificate.length() > 0) { + sslConf = new HTTPServer::SSLConfig(); + sslConf->privateKey = options.certificate; + sslConf->publicKey = options.certificate; + sslConf->port = options.httpsPort; + + } else if (options.privateKey.length() > 0 && options.publicKey.length() > 0) { + sslConf = new HTTPServer::SSLConfig(); + sslConf->privateKey = options.privateKey; + sslConf->publicKey = options.publicKey; + sslConf->port = options.httpsPort; + } + HTTPServer::getInstance(options.httpPort, sslConf); + + // instantiate and configure interpreters + std::list<Interpreter> interpreters; + std::map<std::string, InterpreterOptions*>::iterator confIter = options.interpreters.begin(); + while(confIter != options.interpreters.end()) { + + InterpreterOptions* currOptions = confIter->second; + std::string documentURL = confIter->first; + + LOG(INFO) << "Processing " << documentURL; + Interpreter interpreter = Interpreter::fromURI(documentURL); + if (interpreter) { + interpreter.setCmdLineOptions(currOptions->additionalParameters); + interpreter.setCapabilities(options.getCapabilities()); + + if (options.verbose) { + VerboseMonitor* vm = new VerboseMonitor(); + interpreter.addMonitor(vm); + } + if (options.useDot) { + SCXMLDotWriter* dotWriter = new SCXMLDotWriter(); + interpreter.addMonitor(dotWriter); + } + + interpreters.push_back(interpreter); - if (!glogIsInitialized) - google::InitGoogleLogging(argv[0]); - -// for (int i = 0; i < argc; i++) -// std::cout << argv[i] << std::endl; -// std::cout << optind << std::endl; - - // intialize http server on given port - HTTPServer::getInstance(port); + } else { + LOG(ERROR) << "Cannot create interpreter from " << documentURL; + } + confIter++; + } - LOG(INFO) << "Processing " << argv[optind]; - Interpreter interpreter = Interpreter::fromURI(argv[optind]); - if (interpreter) { - interpreter.setCmdLineOptions(argc, argv); - // interpreter->setCapabilities(Interpreter::CAN_NOTHING); - // interpreter->setCapabilities(Interpreter::CAN_BASIC_HTTP | Interpreter::CAN_GENERIC_HTTP); + // start interpreters + std::list<Interpreter>::iterator interpreterIter = interpreters.begin(); + while(interpreterIter != interpreters.end()) { + interpreterIter->start(); + interpreterIter++; + } - if (verbose) { - VerboseMonitor* vm = new VerboseMonitor(); - interpreter.addMonitor(vm); - } - if (useDot) { - SCXMLDotWriter* dotWriter = new SCXMLDotWriter(); - interpreter.addMonitor(dotWriter); + // call from main thread for UI events + while(interpreters.size() > 0) { + interpreterIter = interpreters.begin(); + while(interpreterIter != interpreters.end()) { + interpreterIter->runOnMainThread(25); + interpreterIter++; } - - interpreter.start(); - while(interpreter.runOnMainThread(25)); } + return EXIT_SUCCESS; }
\ No newline at end of file diff --git a/config.h.in b/config.h.in index 93e4e4a..2498d12 100644 --- a/config.h.in +++ b/config.h.in @@ -66,6 +66,8 @@ #cmakedefine PROTOBUF_FOUND #cmakedefine CORELOCATION_FOUND #cmakedefine LIBPURPLE_FOUND +#cmakedefine OPENSSL_FOUND +#cmakedefine EVENT_SSL_FOUND /** Header files we found */ #cmakedefine HAS_UNISTD_H diff --git a/contrib/cmake/FindEVENT.cmake b/contrib/cmake/FindEVENT.cmake index 7904b6a..418ebab 100644 --- a/contrib/cmake/FindEVENT.cmake +++ b/contrib/cmake/FindEVENT.cmake @@ -30,6 +30,31 @@ else() endif() endif() + + +FIND_LIBRARY(EVENT_SSL_LIBRARY_RELEASE + NAMES event_openssl libevent_openssl + HINTS $ENV{EVENT_SRC}/.libs/ + SET(EVENT_SSL_FOUND ON) +) +if (EVENT_SSL_LIBRARY_RELEASE) + list(APPEND EVENT_LIBRARY optimized ${EVENT_SSL_LIBRARY_RELEASE}) +endif() + +FIND_LIBRARY(EVENT_SSL_LIBRARY_DEBUG + NAMES event_openssl_d libevent_openssl_d + HINTS $ENV{EVENT_SRC}/.libs/ +) +if (EVENT_SSL_LIBRARY_DEBUG) + list(APPEND EVENT_LIBRARY debug ${EVENT_SSL_LIBRARY_DEBUG}) +else() + if (UNIX AND EVENT_SSL_LIBRARY_RELEASE) + list(APPEND EVENT_LIBRARY debug ${EVENT_SSL_LIBRARY_RELEASE}) + endif() +endif() + + + if (NOT WIN32) FIND_LIBRARY(EVENT_LIBRARY_THREADS NAMES event_pthreads @@ -51,6 +76,8 @@ if (NOT WIN32) endif() +# message(FATAL_ERROR "EVENT_LIBRARY: ${EVENT_LIBRARY}") + INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(EVENT DEFAULT_MSG EVENT_LIBRARY EVENT_INCLUDE_DIR) MARK_AS_ADVANCED(EVENT_LIBRARY EVENT_INCLUDE_DIR) diff --git a/contrib/cmake/USCXMLMacros.cmake b/contrib/cmake/USCXMLMacros.cmake index eb325e8..d5ea05f 100644 --- a/contrib/cmake/USCXMLMacros.cmake +++ b/contrib/cmake/USCXMLMacros.cmake @@ -56,3 +56,4 @@ function(INSTALL_EXECUTABLE) COMPONENT ${INSTALL_EXECUTABLE_COMPONENT} PERMISSIONS WORLD_EXECUTE OWNER_EXECUTE GROUP_EXECUTE OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ) endfunction() + diff --git a/contrib/cmake/cotire.cmake b/contrib/cmake/cotire.cmake new file mode 100644 index 0000000..16cb90f --- /dev/null +++ b/contrib/cmake/cotire.cmake @@ -0,0 +1,3239 @@ +# - cotire (compile time reducer) +# +# See the cotire manual for usage hints. +# +#============================================================================= +# Copyright 2012-2013 Sascha Kratky +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +#============================================================================= + +if(__COTIRE_INCLUDED) + return() +endif() +set(__COTIRE_INCLUDED TRUE) + +# call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode +# cmake_minimum_required also sets the policy version as a side effect, which we have to avoid +if (NOT CMAKE_SCRIPT_MODE_FILE) + cmake_policy(PUSH) +endif() +# we need the CMake variables CMAKE_SCRIPT_MODE_FILE and CMAKE_ARGV available since 2.8.5 +# we need APPEND_STRING option for set_property available since 2.8.6 +cmake_minimum_required(VERSION 2.8.6) +if (NOT CMAKE_SCRIPT_MODE_FILE) + cmake_policy(POP) +endif() + +set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}") +set (COTIRE_CMAKE_MODULE_VERSION "1.4.3") + +include(CMakeParseArguments) +include(ProcessorCount) + +function (cotire_determine_compiler_version _language _versionPrefix) + if (NOT ${_versionPrefix}_VERSION) + # use CMake's predefined compiler version variable (available since CMake 2.8.8) + if (DEFINED CMAKE_${_language}_COMPILER_VERSION) + set (${_versionPrefix}_VERSION "${CMAKE_${_language}_COMPILER_VERSION}") + elseif (WIN32) + # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared + unset (ENV{VS_UNICODE_OUTPUT}) + string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1) + execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} + ERROR_VARIABLE _versionLine OUTPUT_QUIET TIMEOUT 10) + string (REGEX REPLACE ".*Version *([0-9]+(\\.[0-9]+)*).*" "\\1" ${_versionPrefix}_VERSION "${_versionLine}") + else() + # assume GCC like command line interface + string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1) + execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} "-dumpversion" + OUTPUT_VARIABLE ${_versionPrefix}_VERSION + RESULT_VARIABLE _result + OUTPUT_STRIP_TRAILING_WHITESPACE TIMEOUT 10) + if (_result) + set (${_versionPrefix}_VERSION "") + endif() + endif() + if (${_versionPrefix}_VERSION) + set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" CACHE INTERNAL "${_language} compiler version") + endif() + set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" PARENT_SCOPE) + if (COTIRE_DEBUG) + message (STATUS "${CMAKE_${_language}_COMPILER} version ${${_versionPrefix}_VERSION}") + endif() + endif() +endfunction() + +function (cotire_get_source_file_extension _sourceFile _extVar) + # get_filename_component returns extension from first occurrence of . in file name + # this function computes the extension from last occurrence of . in file name + string (FIND "${_sourceFile}" "." _index REVERSE) + if (_index GREATER -1) + math (EXPR _index "${_index} + 1") + string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt) + else() + set (_sourceExt "") + endif() + set (${_extVar} "${_sourceExt}" PARENT_SCOPE) +endfunction() + +macro (cotire_check_is_path_relative_to _path _isRelativeVar) + set (${_isRelativeVar} FALSE) + if (IS_ABSOLUTE "${_path}") + foreach (_dir ${ARGN}) + file (RELATIVE_PATH _relPath "${_dir}" "${_path}") + if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")) + set (${_isRelativeVar} TRUE) + break() + endif() + endforeach() + endif() +endmacro() + +function (cotire_filter_language_source_files _language _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar) + set (_sourceFiles "") + set (_excludedSourceFiles "") + set (_cotiredSourceFiles "") + if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) + set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}") + else() + set (_languageExtensions "") + endif() + if (CMAKE_${_language}_IGNORE_EXTENSIONS) + set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}") + else() + set (_ignoreExtensions "") + endif() + if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS) + set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}") + else() + set (_excludeExtensions "") + endif() + if (COTIRE_DEBUG) + message (STATUS "${_language} source file extensions: ${_languageExtensions}") + message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}") + message (STATUS "${_language} exclude extensions: ${_excludeExtensions}") + endif() + foreach (_sourceFile ${ARGN}) + get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY) + get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT) + get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC) + get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE) + set (_sourceIsFiltered FALSE) + if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic) + cotire_get_source_file_extension("${_sourceFile}" _sourceExt) + if (_sourceExt) + list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex) + if (_ignoreIndex LESS 0) + list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex) + if (_excludeIndex GREATER -1) + list (APPEND _excludedSourceFiles "${_sourceFile}") + else() + list (FIND _languageExtensions "${_sourceExt}" _sourceIndex) + if (_sourceIndex GREATER -1) + set (_sourceIsFiltered TRUE) + elseif ("${_sourceLanguage}" STREQUAL "${_language}") + # add to excluded sources, if file is not ignored and has correct language without having the correct extension + list (APPEND _excludedSourceFiles "${_sourceFile}") + endif() + endif() + endif() + endif() + endif() + if (COTIRE_DEBUG) + message (STATUS "${_sourceFile} filtered=${_sourceIsFiltered} language=${_sourceLanguage} header=${_sourceIsHeaderOnly}") + endif() + if (_sourceIsFiltered) + get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED) + get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET) + get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS) + if (COTIRE_DEBUG) + message (STATUS "${_sourceFile} excluded=${_sourceIsExcluded} cotired=${_sourceIsCotired} compileFlags=${_sourceCompileFlags}") + endif() + if (_sourceIsCotired) + list (APPEND _cotiredSourceFiles "${_sourceFile}") + elseif (_sourceIsExcluded OR _sourceCompileFlags) + list (APPEND _excludedSourceFiles "${_sourceFile}") + else() + list (APPEND _sourceFiles "${_sourceFile}") + endif() + endif() + endforeach() + if (COTIRE_DEBUG) + message (STATUS "All: ${ARGN}") + message (STATUS "${_language}: ${_sourceFiles}") + message (STATUS "Excluded: ${_excludedSourceFiles}") + message (STATUS "Cotired: ${_cotiredSourceFiles}") + endif() + set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE) + set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE) + set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE) +endfunction() + +function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type) + set (_filteredObjects "") + foreach (_object ${ARGN}) + get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) + if (_isSet) + get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) + if (_propertyValue) + list (APPEND _filteredObjects "${_object}") + endif() + endif() + endforeach() + set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) +endfunction() + +function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type) + set (_filteredObjects "") + foreach (_object ${ARGN}) + get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) + if (_isSet) + get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) + if (NOT _propertyValue) + list (APPEND _filteredObjects "${_object}") + endif() + endif() + endforeach() + set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_file_property_values _valuesVar _property) + set (_values "") + foreach (_sourceFile ${ARGN}) + get_source_file_property(_propertyValue "${_sourceFile}" ${_property}) + if (_propertyValue) + list (APPEND _values "${_propertyValue}") + endif() + endforeach() + set (${_valuesVar} ${_values} PARENT_SCOPE) +endfunction() + +function (cotrie_resolve_config_properites _configurations _propertiesVar) + set (_properties "") + foreach (_property ${ARGN}) + if ("${_property}" MATCHES "<CONFIG>") + foreach (_config ${_configurations}) + string (TOUPPER "${_config}" _upperConfig) + string (REPLACE "<CONFIG>" "${_upperConfig}" _configProperty "${_property}") + list (APPEND _properties ${_configProperty}) + endforeach() + else() + list (APPEND _properties ${_property}) + endif() + endforeach() + set (${_propertiesVar} ${_properties} PARENT_SCOPE) +endfunction() + +function (cotrie_copy_set_properites _configurations _type _source _target) + cotrie_resolve_config_properites("${_configurations}" _properties ${ARGN}) + foreach (_property ${_properties}) + get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET) + if (_isSet) + get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property}) + set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}") + endif() + endforeach() +endfunction() + +function (cotire_filter_compile_flags _language _flagFilter _matchedOptionsVar _unmatchedOptionsVar) + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + set (_flagPrefix "[/-]") + else() + set (_flagPrefix "--?") + endif() + set (_optionFlag "") + set (_matchedOptions "") + set (_unmatchedOptions "") + foreach (_compileFlag ${ARGN}) + if (_compileFlag) + if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}") + # option with separate argument + list (APPEND _matchedOptions "${_compileFlag}") + set (_optionFlag "") + elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$") + # remember option + set (_optionFlag "${CMAKE_MATCH_2}") + elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$") + # option with joined argument + list (APPEND _matchedOptions "${CMAKE_MATCH_3}") + set (_optionFlag "") + else() + # flush remembered option + if (_optionFlag) + list (APPEND _matchedOptions "${_optionFlag}") + set (_optionFlag "") + endif() + # add to unfiltered options + list (APPEND _unmatchedOptions "${_compileFlag}") + endif() + endif() + endforeach() + if (_optionFlag) + list (APPEND _matchedOptions "${_optionFlag}") + endif() + if (COTIRE_DEBUG) + message (STATUS "Filter ${_flagFilter}") + if (_matchedOptions) + message (STATUS "Matched ${_matchedOptions}") + endif() + if (_unmatchedOptions) + message (STATUS "Unmatched ${_unmatchedOptions}") + endif() + endif() + set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE) + set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_compile_flags _config _language _directory _target _flagsVar) + string (TOUPPER "${_config}" _upperConfig) + # collect options from CMake language variables + set (_compileFlags "") + if (CMAKE_${_language}_FLAGS) + set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}") + endif() + if (CMAKE_${_language}_FLAGS_${_upperConfig}) + set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}") + endif() + if (_target) + # add option from CMake target type variable + get_target_property(_targetType ${_target} TYPE) + if (POLICY CMP0018) + # handle POSITION_INDEPENDENT_CODE property introduced with CMake 2.8.9 if policy CMP0018 is turned on + cmake_policy(GET CMP0018 _PIC_Policy) + else() + # default to old behavior + set (_PIC_Policy "OLD") + endif() + if (COTIRE_DEBUG) + message(STATUS "CMP0018=${_PIC_Policy}") + endif() + if (_PIC_Policy STREQUAL "NEW") + # NEW behavior: honor the POSITION_INDEPENDENT_CODE target property + get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE) + if (_targetPIC) + if (_targetType STREQUAL "EXECUTABLE" AND CMAKE_${_language}_COMPILE_OPTIONS_PIE) + set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIE}") + elseif (CMAKE_${_language}_COMPILE_OPTIONS_PIC) + set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIC}") + endif() + endif() + else() + # OLD behavior or policy not set: use the value of CMAKE_SHARED_LIBRARY_<Lang>_FLAGS + if (_targetType STREQUAL "MODULE_LIBRARY") + # flags variable for module library uses different name SHARED_MODULE + # (e.g., CMAKE_SHARED_MODULE_C_FLAGS) + set (_targetType SHARED_MODULE) + endif() + if (CMAKE_${_targetType}_${_language}_FLAGS) + set (_compileFlags "${_compileFlags} ${CMAKE_${_targetType}_${_language}_FLAGS}") + endif() + endif() + endif() + if (_directory) + # add_definitions may have been used to add flags to the compiler command + get_directory_property(_dirDefinitions DIRECTORY "${_directory}" DEFINITIONS) + if (_dirDefinitions) + set (_compileFlags "${_compileFlags} ${_dirDefinitions}") + endif() + endif() + if (_target) + # add target compile options + get_target_property(_targetflags ${_target} COMPILE_FLAGS) + if (_targetflags) + set (_compileFlags "${_compileFlags} ${_targetflags}") + endif() + endif() + if (UNIX) + separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}") + elseif(WIN32) + separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}") + else() + separate_arguments(_compileFlags) + endif() + # platform specific flags + if (APPLE) + get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig}) + if (NOT _architectures) + get_target_property(_architectures ${_target} OSX_ARCHITECTURES) + endif() + foreach (_arch ${_architectures}) + list (APPEND _compileFlags "-arch" "${_arch}") + endforeach() + if (CMAKE_OSX_SYSROOT AND CMAKE_OSX_SYSROOT_DEFAULT AND CMAKE_${_language}_HAS_ISYSROOT) + if (NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "${CMAKE_OSX_SYSROOT_DEFAULT}") + list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}") + endif() + endif() + if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG) + list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}") + endif() + endif() + if (COTIRE_DEBUG AND _compileFlags) + message (STATUS "Target ${_target} compile flags ${_compileFlags}") + endif() + set (${_flagsVar} ${_compileFlags} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_include_directories _config _language _targetSourceDir _targetBinaryDir _target _includeDirsVar) + set (_includeDirs "") + # default include dirs + if (CMAKE_INCLUDE_CURRENT_DIR) + list (APPEND _includeDirs "${_targetBinaryDir}") + list (APPEND _includeDirs "${_targetSourceDir}") + endif() + # parse additional include directories from target compile flags + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags) + cotire_filter_compile_flags("${_language}" "I" _dirs _ignore ${_targetFlags}) + if (_dirs) + list (APPEND _includeDirs ${_dirs}) + endif() + # target include directories + get_directory_property(_dirs DIRECTORY "${_targetSourceDir}" INCLUDE_DIRECTORIES) + if (_target) + get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES) + if (_targetDirs) + list (APPEND _dirs ${_targetDirs}) + list (REMOVE_DUPLICATES _dirs) + endif() + endif() + list (LENGTH _includeDirs _projectInsertIndex) + foreach (_dir ${_dirs}) + if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE) + cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}") + if (_isRelative) + list (LENGTH _includeDirs _len) + if (_len EQUAL _projectInsertIndex) + list (APPEND _includeDirs "${_dir}") + else() + list (INSERT _includeDirs _projectInsertIndex "${_dir}") + endif() + math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1") + else() + list (APPEND _includeDirs "${_dir}") + endif() + else() + list (APPEND _includeDirs "${_dir}") + endif() + endforeach() + list (REMOVE_DUPLICATES _includeDirs) + if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES) + list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES}) + endif() + if (COTIRE_DEBUG AND _includeDirs) + message (STATUS "Target ${_target} include dirs ${_includeDirs}") + endif() + set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE) +endfunction() + +macro (cotire_make_C_identifier _identifierVar _str) + # mimic CMake SystemTools::MakeCindentifier behavior + if ("${_str}" MATCHES "^[0-9].+$") + set (_str "_${str}") + endif() + string (REGEX REPLACE "[^a-zA-Z0-9]" "_" ${_identifierVar} "${_str}") +endmacro() + +function (cotire_get_target_export_symbol _target _exportSymbolVar) + set (_exportSymbol "") + get_target_property(_targetType ${_target} TYPE) + get_target_property(_enableExports ${_target} ENABLE_EXPORTS) + if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR + (_targetType STREQUAL "EXECUTABLE" AND _enableExports)) + get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL) + if (NOT _exportSymbol) + set (_exportSymbol "${_target}_EXPORTS") + endif() + cotire_make_C_identifier(_exportSymbol "${_exportSymbol}") + endif() + set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_compile_definitions _config _language _directory _target _definitionsVar) + string (TOUPPER "${_config}" _upperConfig) + set (_configDefinitions "") + # CMAKE_INTDIR for multi-configuration build systems + if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".") + list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"") + endif() + # target export define symbol + cotire_get_target_export_symbol("${_target}" _defineSymbol) + if (_defineSymbol) + list (APPEND _configDefinitions "${_defineSymbol}") + endif() + # directory compile definitions + get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS_${_upperConfig}) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + # target compile definitions + get_target_property(_definitions ${_target} COMPILE_DEFINITIONS) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig}) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + # parse additional compile definitions from target compile flags + # and don't look at directory compile definitions, which we already handled + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "" "${_target}" _targetFlags) + cotire_filter_compile_flags("${_language}" "D" _definitions _ignore ${_targetFlags}) + if (_definitions) + list (APPEND _configDefinitions ${_definitions}) + endif() + list (REMOVE_DUPLICATES _configDefinitions) + if (COTIRE_DEBUG AND _configDefinitions) + message (STATUS "Target ${_target} compile definitions ${_configDefinitions}") + endif() + set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) +endfunction() + +function (cotire_get_target_compiler_flags _config _language _directory _target _compilerFlagsVar) + # parse target compile flags omitting compile definitions and include directives + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "${_directory}" "${_target}" _targetFlags) + set (_compilerFlags "") + cotire_filter_compile_flags("${_language}" "[ID]" _ignore _compilerFlags ${_targetFlags}) + if (COTIRE_DEBUG AND _compilerFlags) + message (STATUS "Target ${_target} compiler flags ${_compilerFlags}") + endif() + set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE) +endfunction() + +function (cotire_add_sys_root_paths _pathsVar) + if (APPLE) + if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT) + foreach (_path IN LISTS ${_pathsVar}) + if (IS_ABSOLUTE "${_path}") + get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE) + if (EXISTS "${_path}") + list (APPEND ${_pathsVar} "${_path}") + endif() + endif() + endforeach() + endif() + endif() + set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE) + if (COTIRE_DEBUG) + message (STATUS "${_pathsVar}=${${_pathsVar}}") + endif() +endfunction() + +function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar) + set (_extraProperties ${ARGN}) + set (_result "") + if (_extraProperties) + list (FIND _extraProperties "${_sourceFile}" _index) + if (_index GREATER -1) + math (EXPR _index "${_index} + 1") + list (LENGTH _extraProperties _len) + math (EXPR _len "${_len} - 1") + foreach (_index RANGE ${_index} ${_len}) + list (GET _extraProperties ${_index} _value) + if ("${_value}" MATCHES "${_pattern}") + list (APPEND _result "${_value}") + else() + break() + endif() + endforeach() + endif() + endif() + set (${_resultVar} ${_result} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar) + set (_compileDefinitions "") + if (NOT CMAKE_SCRIPT_MODE_FILE) + string (TOUPPER "${_config}" _upperConfig) + get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS) + if (_definitions) + list (APPEND _compileDefinitions ${_definitions}) + endif() + get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig}) + if (_definitions) + list (APPEND _compileDefinitions ${_definitions}) + endif() + endif() + cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN}) + if (_definitions) + list (APPEND _compileDefinitions ${_definitions}) + endif() + if (COTIRE_DEBUG AND _compileDefinitions) + message (STATUS "Source ${_sourceFile} compile definitions ${_compileDefinitions}") + endif() + set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_files_compile_definitions _config _language _definitionsVar) + set (_configDefinitions "") + foreach (_sourceFile ${ARGN}) + cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions) + if (_sourceDefinitions) + list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-") + endif() + endforeach() + set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar) + set (_sourceUndefs "") + if (NOT CMAKE_SCRIPT_MODE_FILE) + get_source_file_property(_undefs "${_sourceFile}" ${_property}) + if (_undefs) + list (APPEND _sourceUndefs ${_undefs}) + endif() + endif() + cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN}) + if (_undefs) + list (APPEND _sourceUndefs ${_undefs}) + endif() + if (COTIRE_DEBUG AND _sourceUndefs) + message (STATUS "Source ${_sourceFile} ${_property} undefs ${_sourceUndefs}") + endif() + set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) +endfunction() + +function (cotire_get_source_files_undefs _property _sourceUndefsVar) + set (_sourceUndefs "") + foreach (_sourceFile ${ARGN}) + cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs) + if (_undefs) + list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-") + endif() + endforeach() + set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) +endfunction() + +macro (cotire_set_cmd_to_prologue _cmdVar) + set (${_cmdVar} "${CMAKE_COMMAND}") + if (COTIRE_DEBUG) + list (APPEND ${_cmdVar} "--warn-uninitialized") + endif() + list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$<CONFIGURATION>") + if (COTIRE_VERBOSE) + list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON") + elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles") + list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)") + endif() +endmacro() + +function (cotire_init_compile_cmd _cmdVar _language _compilerExe _compilerArg1) + if (NOT _compilerExe) + set (_compilerExe "${CMAKE_${_language}_COMPILER}") + endif() + if (NOT _compilerArg1) + set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1}) + endif() + string (STRIP "${_compilerArg1}" _compilerArg1) + set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE) +endfunction() + +macro (cotire_add_definitions_to_cmd _cmdVar _language) + foreach (_definition ${ARGN}) + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + list (APPEND ${_cmdVar} "/D${_definition}") + else() + list (APPEND ${_cmdVar} "-D${_definition}") + endif() + endforeach() +endmacro() + +macro (cotire_add_includes_to_cmd _cmdVar _language) + foreach (_include ${ARGN}) + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + file (TO_NATIVE_PATH "${_include}" _include) + list (APPEND ${_cmdVar} "/I${_include}") + else() + list (APPEND ${_cmdVar} "-I${_include}") + endif() + endforeach() +endmacro() + +macro (cotire_add_compile_flags_to_cmd _cmdVar) + foreach (_flag ${ARGN}) + list (APPEND ${_cmdVar} "${_flag}") + endforeach() +endmacro() + +function (cotire_check_file_up_to_date _fileIsUpToDateVar _file) + set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE) + set (_triggerFile "") + foreach (_dependencyFile ${ARGN}) + if (EXISTS "${_dependencyFile}" AND "${_dependencyFile}" IS_NEWER_THAN "${_file}") + set (_triggerFile "${_dependencyFile}") + break() + endif() + endforeach() + get_filename_component(_fileName "${_file}" NAME) + if (EXISTS "${_file}") + if (_triggerFile) + if (COTIRE_VERBOSE) + message (STATUS "${_fileName} update triggered by ${_triggerFile} change.") + endif() + else() + if (COTIRE_VERBOSE) + message (STATUS "${_fileName} is up-to-date.") + endif() + set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE) + endif() + else() + if (COTIRE_VERBOSE) + message (STATUS "${_fileName} does not exist yet.") + endif() + endif() +endfunction() + +macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar) + set (${_relPathVar} "") + foreach (_includeDir ${_includeDirs}) + if (IS_DIRECTORY "${_includeDir}") + file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}") + if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.") + string (LENGTH "${${_relPathVar}}" _closestLen) + string (LENGTH "${_relPath}" _relLen) + if (_closestLen EQUAL 0 OR _relLen LESS _closestLen) + set (${_relPathVar} "${_relPath}") + endif() + endif() + elseif ("${_includeDir}" STREQUAL "${_headerFile}") + # if path matches exactly, return short non-empty string + set (${_relPathVar} "1") + break() + endif() + endforeach() +endmacro() + +macro (cotire_check_header_file_location _headerFile _insideIncudeDirs _outsideIncudeDirs _headerIsInside) + # check header path against ignored and honored include directories + cotire_find_closest_relative_path("${_headerFile}" "${_insideIncudeDirs}" _insideRelPath) + if (_insideRelPath) + # header is inside, but could be become outside if there is a shorter outside match + cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncudeDirs}" _outsideRelPath) + if (_outsideRelPath) + string (LENGTH "${_insideRelPath}" _insideRelPathLen) + string (LENGTH "${_outsideRelPath}" _outsideRelPathLen) + if (_outsideRelPathLen LESS _insideRelPathLen) + set (${_headerIsInside} FALSE) + else() + set (${_headerIsInside} TRUE) + endif() + else() + set (${_headerIsInside} TRUE) + endif() + else() + # header is outside + set (${_headerIsInside} FALSE) + endif() +endmacro() + +macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar) + if (NOT EXISTS "${_headerFile}") + set (${_headerIsIgnoredVar} TRUE) + elseif (IS_DIRECTORY "${_headerFile}") + set (${_headerIsIgnoredVar} TRUE) + elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$") + # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path + # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation + # with the error message "error: no include path in which to search for header.h" + set (${_headerIsIgnoredVar} TRUE) + else() + set (${_headerIsIgnoredVar} FALSE) + endif() +endmacro() + +macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar) + # check header file extension + cotire_get_source_file_extension("${_headerFile}" _headerFileExt) + set (${_headerIsIgnoredVar} FALSE) + if (_headerFileExt) + list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index) + if (_index GREATER -1) + set (${_headerIsIgnoredVar} TRUE) + endif() + endif() +endmacro() + +macro (cotire_parse_line _line _headerFileVar _headerDepthVar) + if (MSVC) + # cl.exe /showIncludes output looks different depending on the language pack used, e.g.: + # English: "Note: including file: C:\directory\file" + # German: "Hinweis: Einlesen der Datei: C:\directory\file" + # We use a very general regular expression, relying on the presence of the : characters + if ("${_line}" MATCHES ":( +)([^:]+:[^:]+)$") + # Visual Studio compiler output + string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) + get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE) + else() + set (${_headerFileVar} "") + set (${_headerDepthVar} 0) + endif() + else() + if ("${_line}" MATCHES "^(\\.+) (.*)$") + # GCC like output + string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) + if (IS_ABSOLUTE "${CMAKE_MATCH_2}") + set (${_headerFileVar} "${CMAKE_MATCH_2}") + else() + get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH) + endif() + else() + set (${_headerFileVar} "") + set (${_headerDepthVar} 0) + endif() + endif() +endmacro() + +function (cotire_parse_includes _language _scanOutput _ignoredIncudeDirs _honoredIncudeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar) + if (WIN32) + # prevent CMake macro invocation errors due to backslash characters in Windows paths + string (REPLACE "\\" "/" _scanOutput "${_scanOutput}") + endif() + # canonize slashes + string (REPLACE "//" "/" _scanOutput "${_scanOutput}") + # prevent semicolon from being interpreted as a line separator + string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}") + # then separate lines + string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}") + list (LENGTH _scanOutput _len) + # remove duplicate lines to speed up parsing + list (REMOVE_DUPLICATES _scanOutput) + list (LENGTH _scanOutput _uniqueLen) + if (COTIRE_VERBOSE) + message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes") + if (_ignoredExtensions) + message (STATUS "Ignored extensions: ${_ignoredExtensions}") + endif() + if (_ignoredIncudeDirs) + message (STATUS "Ignored paths: ${_ignoredIncudeDirs}") + endif() + if (_honoredIncudeDirs) + message (STATUS "Included paths: ${_honoredIncudeDirs}") + endif() + endif() + set (_sourceFiles ${ARGN}) + set (_selectedIncludes "") + set (_unparsedLines "") + # stack keeps track of inside/outside project status of processed header files + set (_headerIsInsideStack "") + foreach (_line IN LISTS _scanOutput) + if (_line) + cotire_parse_line("${_line}" _headerFile _headerDepth) + if (_headerFile) + cotire_check_header_file_location("${_headerFile}" "${_ignoredIncudeDirs}" "${_honoredIncudeDirs}" _headerIsInside) + if (COTIRE_DEBUG) + message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}") + endif() + # update stack + list (LENGTH _headerIsInsideStack _stackLen) + if (_headerDepth GREATER _stackLen) + math (EXPR _stackLen "${_stackLen} + 1") + foreach (_index RANGE ${_stackLen} ${_headerDepth}) + list (APPEND _headerIsInsideStack ${_headerIsInside}) + endforeach() + else() + foreach (_index RANGE ${_headerDepth} ${_stackLen}) + list (REMOVE_AT _headerIsInsideStack -1) + endforeach() + list (APPEND _headerIsInsideStack ${_headerIsInside}) + endif() + if (COTIRE_DEBUG) + message (STATUS "${_headerIsInsideStack}") + endif() + # header is a candidate if it is outside project + if (NOT _headerIsInside) + # get parent header file's inside/outside status + if (_headerDepth GREATER 1) + math (EXPR _index "${_headerDepth} - 2") + list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside) + else() + set (_parentHeaderIsInside TRUE) + endif() + # select header file if parent header file is inside project + # (e.g., a project header file that includes a standard header file) + if (_parentHeaderIsInside) + cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored) + if (NOT _headerIsIgnored) + cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored) + if (NOT _headerIsIgnored) + list (APPEND _selectedIncludes "${_headerFile}") + else() + # fix header's inside status on stack, it is ignored by extension now + list (REMOVE_AT _headerIsInsideStack -1) + list (APPEND _headerIsInsideStack TRUE) + endif() + endif() + if (COTIRE_DEBUG) + message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}") + endif() + endif() + endif() + else() + if (MSVC) + # for cl.exe do not keep unparsed lines which solely consist of a source file name + string (FIND "${_sourceFiles}" "${_line}" _index) + if (_index LESS 0) + list (APPEND _unparsedLines "${_line}") + endif() + else() + list (APPEND _unparsedLines "${_line}") + endif() + endif() + endif() + endforeach() + list (REMOVE_DUPLICATES _selectedIncludes) + set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE) + set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE) +endfunction() + +function (cotire_scan_includes _includesVar) + set(_options "") + set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_VERSION LANGUAGE UNPARSED_LINES) + set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) + if (NOT _option_LANGUAGE) + set (_option_LANGUAGE "CXX") + endif() + if (NOT _option_COMPILER_ID) + set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") + endif() + set (_cmd "${_option_COMPILER_EXECUTABLE}" ${_option_COMPILER_ARG1}) + cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") + cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) + cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) + cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES}) + cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd) + # only consider existing source files for scanning + set (_existingSourceFiles "") + foreach (_sourceFile ${_sourceFiles}) + if (EXISTS "${_sourceFile}") + list (APPEND _existingSourceFiles "${_sourceFile}") + endif() + endforeach() + if (NOT _existingSourceFiles) + set (${_includesVar} "" PARENT_SCOPE) + return() + endif() + list (APPEND _cmd ${_existingSourceFiles}) + if (COTIRE_VERBOSE) + message (STATUS "execute_process: ${_cmd}") + endif() + if (_option_COMPILER_ID MATCHES "MSVC") + if (COTIRE_DEBUG) + message (STATUS "clearing VS_UNICODE_OUTPUT") + endif() + # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared + unset (ENV{VS_UNICODE_OUTPUT}) + endif() + execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE _result OUTPUT_QUIET ERROR_VARIABLE _output) + if (_result) + message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.") + endif() + cotire_parse_includes( + "${_option_LANGUAGE}" "${_output}" + "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}" + "${_option_IGNORE_EXTENSIONS}" + _includes _unparsedLines + ${_sourceFiles}) + set (${_includesVar} ${_includes} PARENT_SCOPE) + if (_option_UNPARSED_LINES) + set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE) + endif() +endfunction() + +macro (cotire_append_undefs _contentsVar) + set (_undefs ${ARGN}) + if (_undefs) + list (REMOVE_DUPLICATES _undefs) + foreach (_definition ${_undefs}) + list (APPEND ${_contentsVar} "#undef ${_definition}") + endforeach() + endif() +endmacro() + +macro (cotire_comment_str _language _commentText _commentVar) + if ("${_language}" STREQUAL "CMAKE") + set (${_commentVar} "# ${_commentText}") + else() + set (${_commentVar} "/* ${_commentText} */") + endif() +endmacro() + +function (cotire_write_file _language _file _contents _force) + get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) + cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1) + cotire_comment_str("${_language}" "${_file}" _header2) + set (_contents "${_header1}\n${_header2}\n${_contents}") + if (COTIRE_DEBUG) + message (STATUS "${_contents}") + endif() + if (_force OR NOT EXISTS "${_file}") + file (WRITE "${_file}" "${_contents}") + else() + file (READ "${_file}" _oldContents) + if (NOT "${_oldContents}" STREQUAL "${_contents}") + file (WRITE "${_file}" "${_contents}") + else() + if (COTIRE_DEBUG) + message (STATUS "${_file} unchanged") + endif() + endif() + endif() +endfunction() + +function (cotire_generate_unity_source _unityFile) + set(_options "") + set(_oneValueArgs LANGUAGE) + set(_multiValueArgs + DEPENDS SOURCES_COMPILE_DEFINITIONS + PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS PROLOGUE EPILOGUE) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (_option_DEPENDS) + cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS}) + if (_unityFileIsUpToDate) + return() + endif() + endif() + set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) + if (NOT _option_PRE_UNDEFS) + set (_option_PRE_UNDEFS "") + endif() + if (NOT _option_SOURCES_PRE_UNDEFS) + set (_option_SOURCES_PRE_UNDEFS "") + endif() + if (NOT _option_POST_UNDEFS) + set (_option_POST_UNDEFS "") + endif() + if (NOT _option_SOURCES_POST_UNDEFS) + set (_option_SOURCES_POST_UNDEFS "") + endif() + set (_contents "") + if (_option_PROLOGUE) + list (APPEND _contents ${_option_PROLOGUE}) + endif() + if (_option_LANGUAGE AND _sourceFiles) + if ("${_option_LANGUAGE}" STREQUAL "CXX") + list (APPEND _contents "#ifdef __cplusplus") + elseif ("${_option_LANGUAGE}" STREQUAL "C") + list (APPEND _contents "#ifndef __cplusplus") + endif() + endif() + set (_compileUndefinitions "") + foreach (_sourceFile ${_sourceFiles}) + cotire_get_source_compile_definitions( + "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions + ${_option_SOURCES_COMPILE_DEFINITIONS}) + cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS}) + cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS}) + if (_option_PRE_UNDEFS) + list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS}) + endif() + if (_sourcePreUndefs) + list (APPEND _compileUndefinitions ${_sourcePreUndefs}) + endif() + if (_compileUndefinitions) + cotire_append_undefs(_contents ${_compileUndefinitions}) + set (_compileUndefinitions "") + endif() + if (_sourcePostUndefs) + list (APPEND _compileUndefinitions ${_sourcePostUndefs}) + endif() + if (_option_POST_UNDEFS) + list (APPEND _compileUndefinitions ${_option_POST_UNDEFS}) + endif() + foreach (_definition ${_compileDefinitions}) + if ("${_definition}" MATCHES "^([a-zA-Z0-9_]+)=(.+)$") + list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}") + list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}") + else() + list (APPEND _contents "#define ${_definition}") + list (INSERT _compileUndefinitions 0 "${_definition}") + endif() + endforeach() + get_filename_component(_sourceFile "${_sourceFile}" ABSOLUTE) + if (WIN32) + file (TO_NATIVE_PATH "${_sourceFile}" _sourceFile) + endif() + list (APPEND _contents "#include \"${_sourceFile}\"") + endforeach() + if (_compileUndefinitions) + cotire_append_undefs(_contents ${_compileUndefinitions}) + set (_compileUndefinitions "") + endif() + if (_option_LANGUAGE AND _sourceFiles) + list (APPEND _contents "#endif") + endif() + if (_option_EPILOGUE) + list (APPEND _contents ${_option_EPILOGUE}) + endif() + list (APPEND _contents "") + string (REPLACE ";" "\n" _contents "${_contents}") + if (COTIRE_VERBOSE) + message ("${_contents}") + endif() + cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE) +endfunction() + +function (cotire_generate_prefix_header _prefixFile) + set(_options "") + set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION) + set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS + INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (_option_DEPENDS) + cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS}) + if (_prefixFileIsUpToDate) + return() + endif() + endif() + set (_epilogue "") + if (_option_COMPILER_ID MATCHES "Intel") + # Intel compiler requires hdrstop pragma to stop generating PCH file + set (_epilogue "#pragma hdrstop") + endif() + set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) + cotire_scan_includes(_selectedHeaders ${_sourceFiles} + LANGUAGE "${_option_LANGUAGE}" + COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}" + COMPILER_ID "${_option_COMPILER_ID}" + COMPILER_VERSION "${_option_COMPILER_VERSION}" + COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS} + COMPILE_FLAGS ${_option_COMPILE_FLAGS} + INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES} + IGNORE_PATH ${_option_IGNORE_PATH} + INCLUDE_PATH ${_option_INCLUDE_PATH} + IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS} + UNPARSED_LINES _unparsedLines) + cotire_generate_unity_source("${_prefixFile}" EPILOGUE ${_epilogue} LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders}) + set (_unparsedLinesFile "${_prefixFile}.log") + if (_unparsedLines) + if (COTIRE_VERBOSE OR NOT _selectedHeaders) + list (LENGTH _unparsedLines _skippedLineCount) + file (RELATIVE_PATH _unparsedLinesFileRelPath "${CMAKE_BINARY_DIR}" "${_unparsedLinesFile}") + message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesFileRelPath}") + endif() + string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}") + endif() + file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}\n") +endfunction() + +function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar) + set (_flags ${${_flagsVar}}) + if (_compilerID MATCHES "MSVC") + # cl.exe options used + # /nologo suppresses display of sign-on banner + # /TC treat all files named on the command line as C source files + # /TP treat all files named on the command line as C++ source files + # /EP preprocess to stdout without #line directives + # /showIncludes list include files + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes) + else() + # return as a flag string + set (_flags "${_sourceFileType${_language}} /EP /showIncludes") + endif() + elseif (_compilerID MATCHES "GNU") + # GCC options used + # -H print the name of each header file used + # -E invoke preprocessor + # -fdirectives-only do not expand macros, requires GCC >= 4.3 + if (_flags) + # append to list + list (APPEND _flags -H -E) + if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") + list (APPEND _flags "-fdirectives-only") + endif() + else() + # return as a flag string + set (_flags "-H -E") + if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") + set (_flags "${_flags} -fdirectives-only") + endif() + endif() + elseif (_compilerID MATCHES "Clang") + # Clang options used + # -H print the name of each header file used + # -E invoke preprocessor + if (_flags) + # append to list + list (APPEND _flags -H -E) + else() + # return as a flag string + set (_flags "-H -E") + endif() + elseif (_compilerID MATCHES "Intel") + if (WIN32) + # Windows Intel options used + # /nologo do not display compiler version information + # /QH display the include file order + # /EP preprocess to stdout, omitting #line directives + # /TC process all source or unrecognized file types as C source files + # /TP process all source or unrecognized file types as C++ source files + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /QH) + else() + # return as a flag string + set (_flags "${_sourceFileType${_language}} /EP /QH") + endif() + else() + # Linux / Mac OS X Intel options used + # -H print the name of each header file used + # -EP preprocess to stdout, omitting #line directives + # -Kc++ process all source or unrecognized file types as C++ source files + if (_flags) + # append to list + if ("${_language}" STREQUAL "CXX") + list (APPEND _flags -Kc++) + endif() + list (APPEND _flags -H -EP) + else() + # return as a flag string + if ("${_language}" STREQUAL "CXX") + set (_flags "-Kc++ ") + endif() + set (_flags "${_flags}-H -EP") + endif() + endif() + else() + message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") + endif() + set (${_flagsVar} ${_flags} PARENT_SCOPE) +endfunction() + +function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar) + set (_flags ${${_flagsVar}}) + if (_compilerID MATCHES "MSVC") + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) + # cl.exe options used + # /Yc creates a precompiled header file + # /Fp specifies precompiled header binary file name + # /FI forces inclusion of file + # /TC treat all files named on the command line as C source files + # /TP treat all files named on the command line as C++ source files + # /Zs syntax check only + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" + "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") + else() + # return as a flag string + set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + endif() + elseif (_compilerID MATCHES "GNU|Clang") + # GCC / Clang options used + # -x specify the source language + # -c compile but do not link + # -o place output in file + set (_xLanguage_C "c-header") + set (_xLanguage_CXX "c++-header") + if (_flags) + # append to list + list (APPEND _flags "-x" "${_xLanguage_${_language}}" "-c" "${_prefixFile}" -o "${_pchFile}") + else() + # return as a flag string + set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"") + endif() + elseif (_compilerID MATCHES "Intel") + if (WIN32) + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) + # Windows Intel options used + # /nologo do not display compiler version information + # /Yc create a precompiled header (PCH) file + # /Fp specify a path or file name for precompiled header files + # /FI tells the preprocessor to include a specified file name as the header file + # /TC process all source or unrecognized file types as C source files + # /TP process all source or unrecognized file types as C++ source files + # /Zs syntax check only + # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + set (_sourceFileTypeC "/TC") + set (_sourceFileTypeCXX "/TP") + if (_flags) + # append to list + list (APPEND _flags /nologo "${_sourceFileType${_language}}" + "/Yc" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + list (APPEND _flags "/Wpch-messages") + endif() + else() + # return as a flag string + set (_flags "/Yc /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + set (_flags "${_flags} /Wpch-messages") + endif() + endif() + else() + # Linux / Mac OS X Intel options used + # -pch-dir location for precompiled header files + # -pch-create name of the precompiled header (PCH) to create + # -Kc++ process all source or unrecognized file types as C++ source files + # -fsyntax-only check only for correct syntax + # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + get_filename_component(_pchDir "${_pchFile}" PATH) + get_filename_component(_pchName "${_pchFile}" NAME) + set (_xLanguage_C "c-header") + set (_xLanguage_CXX "c++-header") + if (_flags) + # append to list + if ("${_language}" STREQUAL "CXX") + list (APPEND _flags -Kc++) + endif() + list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-create" "${_pchName}" "-fsyntax-only" "${_hostFile}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + list (APPEND _flags "-Wpch-messages") + endif() + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-create \"${_pchName}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + set (_flags "${_flags} -Wpch-messages") + endif() + endif() + endif() + else() + message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") + endif() + set (${_flagsVar} ${_flags} PARENT_SCOPE) +endfunction() + +function (cotire_add_prefix_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar) + set (_flags ${${_flagsVar}}) + if (_compilerID MATCHES "MSVC") + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + # cl.exe options used + # /Yu uses a precompiled header file during build + # /Fp specifies precompiled header binary file name + # /FI forces inclusion of file + if (_pchFile) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + if (_flags) + # append to list + list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") + else() + # return as a flag string + set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + endif() + else() + # no precompiled header, force inclusion of prefix header + if (_flags) + # append to list + list (APPEND _flags "/FI${_prefixFileNative}") + else() + # return as a flag string + set (_flags "/FI\"${_prefixFileNative}\"") + endif() + endif() + elseif (_compilerID MATCHES "GNU") + # GCC options used + # -include process include file as the first line of the primary source file + # -Winvalid-pch warns if precompiled header is found but cannot be used + if (_flags) + # append to list + list (APPEND _flags "-include" "${_prefixFile}" "-Winvalid-pch") + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\" -Winvalid-pch") + endif() + elseif (_compilerID MATCHES "Clang") + # Clang options used + # -include process include file as the first line of the primary source file + # -Qunused-arguments don't emit warning for unused driver arguments + if (_flags) + # append to list + list (APPEND _flags "-include" "${_prefixFile}" "-Qunused-arguments") + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\" -Qunused-arguments") + endif() + elseif (_compilerID MATCHES "Intel") + if (WIN32) + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + # Windows Intel options used + # /Yu use a precompiled header (PCH) file + # /Fp specify a path or file name for precompiled header files + # /FI tells the preprocessor to include a specified file name as the header file + # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + if (_pchFile) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + if (_flags) + # append to list + list (APPEND _flags "/Yu" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + list (APPEND _flags "/Wpch-messages") + endif() + else() + # return as a flag string + set (_flags "/Yu /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + set (_flags "${_flags} /Wpch-messages") + endif() + endif() + else() + # no precompiled header, force inclusion of prefix header + if (_flags) + # append to list + list (APPEND _flags "/FI${_prefixFileNative}") + else() + # return as a flag string + set (_flags "/FI\"${_prefixFileNative}\"") + endif() + endif() + else() + # Linux / Mac OS X Intel options used + # -pch-dir location for precompiled header files + # -pch-use name of the precompiled header (PCH) to use + # -include process include file as the first line of the primary source file + # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) + if (_pchFile) + get_filename_component(_pchDir "${_pchFile}" PATH) + get_filename_component(_pchName "${_pchFile}" NAME) + if (_flags) + # append to list + list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-use" "${_pchName}") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + list (APPEND _flags "-Wpch-messages") + endif() + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-use \"${_pchName}\"") + if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") + set (_flags "${_flags} -Wpch-messages") + endif() + endif() + else() + # no precompiled header, force inclusion of prefix header + if (_flags) + # append to list + list (APPEND _flags "-include" "${_prefixFile}") + else() + # return as a flag string + set (_flags "-include \"${_prefixFile}\"") + endif() + endif() + endif() + else() + message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") + endif() + set (${_flagsVar} ${_flags} PARENT_SCOPE) +endfunction() + +function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile) + set(_options "") + set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION LANGUAGE) + set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (NOT _option_LANGUAGE) + set (_option_LANGUAGE "CXX") + endif() + if (NOT _option_COMPILER_ID) + set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") + endif() + cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") + cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) + cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) + cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES}) + cotire_add_pch_compilation_flags( + "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd) + if (COTIRE_VERBOSE) + message (STATUS "execute_process: ${_cmd}") + endif() + if (_option_COMPILER_ID MATCHES "MSVC") + if (COTIRE_DEBUG) + message (STATUS "clearing VS_UNICODE_OUTPUT") + endif() + # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared + unset (ENV{VS_UNICODE_OUTPUT}) + endif() + execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE _result) + if (_result) + message (FATAL_ERROR "Error ${_result} precompiling ${_prefixFile}.") + endif() +endfunction() + +function (cotire_check_precompiled_header_support _language _targetSourceDir _target _msgVar) + set (_unsupportedCompiler + "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}") + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") + # supported since Visual Studio C++ 6.0 + # and CMake does not support an earlier version + set (${_msgVar} "" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU") + # GCC PCH support requires version >= 3.4 + cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) + if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND + "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0") + set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) + else() + set (${_msgVar} "" PARENT_SCOPE) + endif() + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang") + # all Clang versions have PCH support + set (${_msgVar} "" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") + # Intel PCH support requires version >= 8.0.0 + cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) + if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND + "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "8.0.0") + set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) + else() + set (${_msgVar} "" PARENT_SCOPE) + endif() + else() + set (${_msgVar} "${_unsupportedCompiler}." PARENT_SCOPE) + endif() + if (APPLE) + # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64) + if (CMAKE_CONFIGURATION_TYPES) + set (_configs ${CMAKE_CONFIGURATION_TYPES}) + elseif (CMAKE_BUILD_TYPE) + set (_configs ${CMAKE_BUILD_TYPE}) + else() + set (_configs "None") + endif() + foreach (_config ${_configs}) + set (_targetFlags "") + cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags) + cotire_filter_compile_flags("${_language}" "arch" _architectures _ignore ${_targetFlags}) + list (LENGTH _architectures _numberOfArchitectures) + if (_numberOfArchitectures GREATER 1) + string (REPLACE ";" ", " _architectureStr "${_architectures}") + set (${_msgVar} + "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})." + PARENT_SCOPE) + break() + endif() + endforeach() + endif() +endfunction() + +macro (cotire_get_intermediate_dir _cotireDir) + get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE) +endmacro() + +macro (cotire_setup_file_extension_variables) + set (_unityFileExt_C ".c") + set (_unityFileExt_CXX ".cxx") + set (_prefixFileExt_C ".h") + set (_prefixFileExt_CXX ".hxx") +endmacro() + +function (cotire_make_single_unity_source_file_path _language _target _unityFileVar) + cotire_setup_file_extension_variables() + if (NOT DEFINED _unityFileExt_${_language}) + set (${_unityFileVar} "" PARENT_SCOPE) + return() + endif() + set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") + set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}") + cotire_get_intermediate_dir(_baseDir) + set (_unityFile "${_baseDir}/${_unityFileName}") + set (${_unityFileVar} "${_unityFile}" PARENT_SCOPE) + if (COTIRE_DEBUG) + message(STATUS "${_unityFile}") + endif() +endfunction() + +function (cotire_make_unity_source_file_paths _language _target _maxIncludes _unityFilesVar) + cotire_setup_file_extension_variables() + if (NOT DEFINED _unityFileExt_${_language}) + set (${_unityFileVar} "" PARENT_SCOPE) + return() + endif() + set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") + cotire_get_intermediate_dir(_baseDir) + set (_startIndex 0) + set (_index 0) + set (_unityFiles "") + set (_sourceFiles ${ARGN}) + foreach (_sourceFile ${_sourceFiles}) + get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE) + math (EXPR _unityFileCount "${_index} - ${_startIndex}") + if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes)) + if (_index GREATER 0) + # start new unity file segment + math (EXPR _endIndex "${_index} - 1") + set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") + list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") + endif() + set (_startIndex ${_index}) + endif() + math (EXPR _index "${_index} + 1") + endforeach() + list (LENGTH _sourceFiles _numberOfSources) + if (_startIndex EQUAL 0) + # there is only a single unity file + cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFiles) + elseif (_startIndex LESS _numberOfSources) + # end with final unity file segment + math (EXPR _endIndex "${_index} - 1") + set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") + list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") + endif() + set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE) + if (COTIRE_DEBUG) + message(STATUS "${_unityFiles}") + endif() +endfunction() + +function (cotire_unity_to_prefix_file_path _language _target _unityFile _prefixFileVar) + cotire_setup_file_extension_variables() + if (NOT DEFINED _unityFileExt_${_language}) + set (${_prefixFileVar} "" PARENT_SCOPE) + return() + endif() + set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") + set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") + string (REPLACE "${_unityFileBaseName}" "${_prefixFileBaseName}" _prefixFile "${_unityFile}") + string (REGEX REPLACE "${_unityFileExt_${_language}}$" "${_prefixFileExt_${_language}}" _prefixFile "${_prefixFile}") + set (${_prefixFileVar} "${_prefixFile}" PARENT_SCOPE) +endfunction() + +function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar) + cotire_setup_file_extension_variables() + if (NOT _language) + set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") + set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}") + elseif (DEFINED _prefixFileExt_${_language}) + set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") + set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}") + else() + set (_prefixFileBaseName "") + set (_prefixFileName "") + endif() + set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE) + set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE) +endfunction() + +function (cotire_make_prefix_file_path _language _target _prefixFileVar) + cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) + set (${_prefixFileVar} "" PARENT_SCOPE) + if (_prefixFileName) + if (NOT _language) + set (_language "C") + endif() + if (MSVC OR CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang|Intel") + cotire_get_intermediate_dir(_baseDir) + set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE) + endif() + endif() +endfunction() + +function (cotire_make_pch_file_path _language _targetSourceDir _target _pchFileVar) + cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) + set (${_pchFileVar} "" PARENT_SCOPE) + if (_prefixFileBaseName AND _prefixFileName) + cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _msg) + if (NOT _msg) + if (XCODE) + # For Xcode, we completely hand off the compilation of the prefix header to the IDE + return() + endif() + cotire_get_intermediate_dir(_baseDir) + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") + # MSVC uses the extension .pch added to the prefix header base name + set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") + # GCC / Clang look for a precompiled header corresponding to the prefix header with the extension .gch appended + set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE) + elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") + # Intel uses the extension .pchi added to the prefix header base name + set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pchi" PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + +function (cotire_select_unity_source_files _unityFile _sourcesVar) + set (_sourceFiles ${ARGN}) + if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)") + set (_startIndex ${CMAKE_MATCH_1}) + set (_endIndex ${CMAKE_MATCH_2}) + list (LENGTH _sourceFiles _numberOfSources) + if (NOT _startIndex LESS _numberOfSources) + math (EXPR _startIndex "${_numberOfSources} - 1") + endif() + if (NOT _endIndex LESS _numberOfSources) + math (EXPR _endIndex "${_numberOfSources} - 1") + endif() + set (_files "") + foreach (_index RANGE ${_startIndex} ${_endIndex}) + list (GET _sourceFiles ${_index} _file) + list (APPEND _files "${_file}") + endforeach() + else() + set (_files ${_sourceFiles}) + endif() + set (${_sourcesVar} ${_files} PARENT_SCOPE) +endfunction() + +function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar) + set (_dependencySources "") + # depend on target's generated source files + cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${ARGN}) + if (_generatedSources) + # but omit all generated source files that have the COTIRE_EXCLUDED property set to true + cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources}) + if (_excludedGeneratedSources) + list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources}) + endif() + # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly + cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources}) + if (_excludedNonDependencySources) + list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources}) + endif() + if (_generatedSources) + list (APPEND _dependencySources ${_generatedSources}) + endif() + endif() + if (COTIRE_DEBUG AND _dependencySources) + message (STATUS "${_language} ${_target} unity source depends on ${_dependencySources}") + endif() + set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) +endfunction() + +function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar) + # depend on target source files marked with custom COTIRE_DEPENDENCY property + set (_dependencySources "") + cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${ARGN}) + if (COTIRE_DEBUG AND _dependencySources) + message (STATUS "${_language} ${_target} prefix header DEPENDS ${_dependencySources}") + endif() + set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) +endfunction() + +function (cotire_generate_target_script _language _configurations _targetSourceDir _targetBinaryDir _target _targetScriptVar) + set (COTIRE_TARGET_SOURCES ${ARGN}) + get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) + set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}") + cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${COTIRE_TARGET_SOURCES}) + cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${COTIRE_TARGET_SOURCES}) + # set up variables to be configured + set (COTIRE_TARGET_LANGUAGE "${_language}") + cotire_determine_compiler_version("${COTIRE_TARGET_LANGUAGE}" COTIRE_${_language}_COMPILER) + get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH) + cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH) + get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH) + cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH) + get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS) + get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS) + get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) + cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${COTIRE_TARGET_SOURCES}) + cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${COTIRE_TARGET_SOURCES}) + set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}") + foreach (_config ${_configurations}) + string (TOUPPER "${_config}" _upperConfig) + cotire_get_target_include_directories( + "${_config}" "${_language}" "${_targetSourceDir}" "${_targetBinaryDir}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}) + cotire_get_target_compile_definitions( + "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}) + cotire_get_target_compiler_flags( + "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}) + cotire_get_source_files_compile_definitions( + "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${COTIRE_TARGET_SOURCES}) + endforeach() + get_cmake_property(_vars VARIABLES) + string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}") + # remove COTIRE_VERBOSE which is passed as a CMake define on command line + list (REMOVE_ITEM _matchVars COTIRE_VERBOSE) + set (_contents "") + foreach (_var IN LISTS _matchVars ITEMS + MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES + CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1 + CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) + if (DEFINED ${_var}) + string (REPLACE "\"" "\\\"" _value "${${_var}}") + set (_contents "${_contents}set (${_var} \"${_value}\")\n") + endif() + endforeach() + cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE) + set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE) +endfunction() + +function (cotire_setup_pch_file_compilation _language _target _targetSourceDir _targetScript _prefixFile _pchFile) + set (_sourceFiles ${ARGN}) + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # for Visual Studio and Intel, we attach the precompiled header compilation to the first source file + # the remaining files include the precompiled header, see cotire_setup_pch_file_inclusion + if (_sourceFiles) + file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) + file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) + list (GET _sourceFiles 0 _hostFile) + set (_flags "") + cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) + cotire_add_pch_compilation_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags) + set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}") + # make first source file depend on prefix header + set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") + # mark first source file as cotired to prevent it from being used in another cotired target + set_property (SOURCE ${_hostFile} PROPERTY COTIRE_TARGET "${_target}") + endif() + elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") + # for makefile based generator, we add a custom command to precompile the prefix header + if (_targetScript) + cotire_set_cmd_to_prologue(_cmds) + list (GET _sourceFiles 0 _hostFile) + list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}") + file (RELATIVE_PATH _pchFileRelPath "${CMAKE_BINARY_DIR}" "${_pchFile}") + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} IMPLICIT_DEPENDS ${_language} ${_prefixFile}") + endif() + set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE) + add_custom_command(OUTPUT "${_pchFile}" + COMMAND ${_cmds} + DEPENDS "${_prefixFile}" + IMPLICIT_DEPENDS ${_language} "${_prefixFile}" + WORKING_DIRECTORY "${_targetSourceDir}" + COMMENT "Building ${_language} precompiled header ${_pchFileRelPath}" VERBATIM) + endif() + endif() +endfunction() + +function (cotire_setup_pch_file_inclusion _language _target _wholeTarget _prefixFile _pchFile) + set (_sourceFiles ${ARGN}) + if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # for Visual Studio and Intel, we include the precompiled header in all but the first source file + # the first source file does the precompiled header compilation, see cotire_setup_pch_file_compilation + list (LENGTH _sourceFiles _numberOfSourceFiles) + if (_numberOfSourceFiles GREATER 1) + # mark sources as cotired to prevent them from being used in another cotired target + set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") + list (REMOVE_AT _sourceFiles 0) + set (_flags "") + cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" _flags) + set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + # make source files depend on precompiled header + set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") + endif() + elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") + if (NOT _wholeTarget) + # for makefile based generator, we force the inclusion of the prefix header for a subset + # of the source files, if this is a multi-language target or has excluded files + set (_flags "") + cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" _flags) + set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + # mark sources as cotired to prevent them from being used in another cotired target + set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") + endif() + # make source files depend on precompiled header + set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") + endif() +endfunction() + +function (cotire_setup_prefix_file_inclusion _language _target _prefixFile) + set (_sourceFiles ${ARGN}) + # force the inclusion of the prefix header for the given source files + set (_flags "") + cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "" _flags) + set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + # mark sources as cotired to prevent them from being used in another cotired target + set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") + # make source files depend on prefix header + set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") +endfunction() + +function (cotire_get_first_set_property_value _propertyValueVar _type _object) + set (_properties ${ARGN}) + foreach (_property ${_properties}) + get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) + if (_propertyValue) + set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE) + return() + endif() + endforeach() + set (${_propertyValueVar} "" PARENT_SCOPE) +endfunction() + +function (cotire_setup_combine_command _language _sourceDir _targetScript _joinedFile _cmdsVar) + set (_files ${ARGN}) + set (_filesPaths "") + foreach (_file ${_files}) + if (IS_ABSOLUTE "${_file}") + set (_filePath "${_file}") + else() + get_filename_component(_filePath "${_sourceDir}/${_file}" ABSOLUTE) + endif() + file (RELATIVE_PATH _fileRelPath "${_sourceDir}" "${_filePath}") + if (NOT IS_ABSOLUTE "${_fileRelPath}" AND NOT "${_fileRelPath}" MATCHES "^\\.\\.") + list (APPEND _filesPaths "${_fileRelPath}") + else() + list (APPEND _filesPaths "${_filePath}") + endif() + endforeach() + cotire_set_cmd_to_prologue(_prefixCmd) + list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine") + if (_targetScript) + list (APPEND _prefixCmd "${_targetScript}") + endif() + list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths}) + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}") + endif() + set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE) + file (RELATIVE_PATH _joinedFileRelPath "${CMAKE_BINARY_DIR}" "${_joinedFile}") + get_filename_component(_joinedFileName "${_joinedFileRelPath}" NAME_WE) + if (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$") + set (_comment "Generating ${_language} unity source ${_joinedFileRelPath}") + elseif (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$") + set (_comment "Generating ${_language} prefix header ${_joinedFileRelPath}") + else() + set (_comment "Generating ${_joinedFileRelPath}") + endif() + add_custom_command( + OUTPUT "${_joinedFile}" + COMMAND ${_prefixCmd} + DEPENDS ${_files} + COMMENT "${_comment}" + WORKING_DIRECTORY "${_sourceDir}" VERBATIM) + list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_setup_target_pch_usage _languages _targetSourceDir _target _wholeTarget) + if (XCODE) + # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers + # if necessary, we also generate a single prefix header which includes all language specific prefix headers + set (_prefixFiles "") + foreach (_language ${_languages}) + get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) + if (_prefixFile) + list (APPEND _prefixFiles "${_prefixFile}") + endif() + endforeach() + set (_cmds ${ARGN}) + list (LENGTH _prefixFiles _numberOfPrefixFiles) + if (_numberOfPrefixFiles GREATER 1) + cotire_make_prefix_file_path("" ${_target} _prefixHeader) + cotire_setup_combine_command("" "${_targetSourceDir}" "" "${_prefixHeader}" _cmds ${_prefixFiles}) + else() + set (_prefixHeader "${_prefixFiles}") + endif() + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}") + endif() + add_custom_command(TARGET "${_target}" + PRE_BUILD ${_cmds} + WORKING_DIRECTORY "${_targetSourceDir}" + COMMENT "Updating target ${_target} prefix headers" VERBATIM) + # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++ + set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES") + set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}") + elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") + # for makefile based generator, we force inclusion of the prefix header for all target source files + # if this is a single-language target without any excluded files + if (_wholeTarget) + set (_language "${_languages}") + # for Visual Studio and Intel, precompiled header inclusion is always done on the source file level + # see cotire_setup_pch_file_inclusion + if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) + get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER) + set (_flags "") + cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) + cotire_add_prefix_pch_inclusion_flags( + "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" + "${_prefixFile}" "${_pchFile}" _flags) + set_property (TARGET ${_target} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") + endif() + endif() + endif() +endfunction() + +function (cotire_setup_unity_generation_commands _language _targetSourceDir _target _targetScript _unityFiles _cmdsVar) + set (_dependencySources "") + cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN}) + foreach (_unityFile ${_unityFiles}) + file (RELATIVE_PATH _unityFileRelPath "${CMAKE_BINARY_DIR}" "${_unityFile}") + set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE) + # set up compiled unity source dependencies + # this ensures that missing source files are generated before the unity file is compiled + if (COTIRE_DEBUG AND _dependencySources) + message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}") + endif() + if (_dependencySources) + set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_dependencySources}) + endif() + if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # unity file compilation results in potentially huge object file, thus use /bigobj by default unter MSVC and Windows Intel + set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj") + endif() + cotire_set_cmd_to_prologue(_unityCmd) + list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetScript}" "${_unityFile}") + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_targetScript}") + endif() + add_custom_command( + OUTPUT "${_unityFile}" + COMMAND ${_unityCmd} + DEPENDS "${_targetScript}" + COMMENT "Generating ${_language} unity source ${_unityFileRelPath}" + WORKING_DIRECTORY "${_targetSourceDir}" VERBATIM) + list (APPEND ${_cmdsVar} COMMAND ${_unityCmd}) + endforeach() + list (LENGTH _unityFiles _numberOfUnityFiles) + if (_numberOfUnityFiles GREATER 1) + # create a joint unity file from all unity file segments + cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile) + cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_unityFile}" ${_cmdsVar} ${_unityFiles}) + endif() + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_setup_single_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFile _cmdsVar) + set (_sourceFiles ${ARGN}) + set (_dependencySources "") + cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles}) + cotire_set_cmd_to_prologue(_prefixCmd) + list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" "${_unityFile}") + set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE) + if (COTIRE_DEBUG) + message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_targetScript} ${_unityFile} ${_dependencySources}") + endif() + file (RELATIVE_PATH _prefixFileRelPath "${CMAKE_BINARY_DIR}" "${_prefixFile}") + add_custom_command( + OUTPUT "${_prefixFile}" "${_prefixFile}.log" + COMMAND ${_prefixCmd} + DEPENDS "${_targetScript}" "${_unityFile}" ${_dependencySources} + COMMENT "Generating ${_language} prefix header ${_prefixFileRelPath}" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) + list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_setup_multi_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFiles _cmdsVar) + set (_sourceFiles ${ARGN}) + list (LENGTH _unityFiles _numberOfUnityFiles) + if (_numberOfUnityFiles GREATER 1) + cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile) + cotire_setup_single_prefix_generation_command( + ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" + "${_prefixFile}" "${_unityFile}" ${_cmdsVar} ${_sourceFiles}) + else() + cotire_setup_single_prefix_generation_command( + ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" + "${_prefixFile}" "${_unityFiles}" ${_cmdsVar} ${_sourceFiles}) + endif() + set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) +endfunction() + +function (cotire_init_cotire_target_properties _target) + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE) + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE) + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE) + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}") + cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}") + if (NOT _isRelative) + set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}") + endif() + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET) + if (NOT _isSet) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "") + endif() + get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET) + if (NOT _isSet) + if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}") + else() + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "") + endif() + endif() +endfunction() + +function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar) + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) + string (REPLACE ";" " " _languagesStr "${_languages}") + string (REPLACE ";" ", " _excludedStr "${ARGN}") + set (_targetMsg "") + if (NOT _languages) + set (_targetMsg "Target ${_target} cannot be cotired.") + if (_disableMsg) + set (_targetMsg "${_targetMsg} ${_disableMsg}") + endif() + elseif (NOT _targetUsePCH AND NOT _targetAddSCU) + set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.") + if (_disableMsg) + set (_targetMsg "${_targetMsg} ${_disableMsg}") + endif() + elseif (NOT _targetUsePCH) + if (_allExcludedSourceFiles) + set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without precompiled header.") + else() + set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.") + endif() + if (_disableMsg) + set (_targetMsg "${_targetMsg} ${_disableMsg}") + endif() + elseif (NOT _targetAddSCU) + if (_allExcludedSourceFiles) + set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without unity build.") + else() + set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.") + endif() + else() + if (_allExcludedSourceFiles) + set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr}.") + else() + set (_targetMsg "${_languagesStr} target ${_target} cotired.") + endif() + endif() + set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE) +endfunction() + +function (cotire_choose_target_languages _targetSourceDir _target _targetLanguagesVar) + set (_languages ${ARGN}) + set (_allSourceFiles "") + set (_allExcludedSourceFiles "") + set (_allCotiredSourceFiles "") + set (_targetLanguages "") + get_target_property(_targetType ${_target} TYPE) + get_target_property(_targetSourceFiles ${_target} SOURCES) + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) + set (_disableMsg "") + foreach (_language ${_languages}) + get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER) + get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE) + if (_prefixHeader OR _unityBuildFile) + message (WARNING "Target ${_target} has already been cotired.") + set (${_targetLanguagesVar} "" PARENT_SCOPE) + return() + endif() + if (_targetUsePCH AND "${_language}" STREQUAL "C" OR "${_language}" STREQUAL "CXX") + cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _disableMsg) + if (_disableMsg) + set (_targetUsePCH FALSE) + endif() + endif() + set (_sourceFiles "") + set (_excludedSources "") + set (_cotiredSources "") + cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) + if (_sourceFiles OR _excludedSources OR _cotiredSources) + list (APPEND _targetLanguages ${_language}) + endif() + if (_sourceFiles) + list (APPEND _allSourceFiles ${_sourceFiles}) + endif() + if (_excludedSources) + list (APPEND _allExcludedSourceFiles ${_excludedSources}) + endif() + if (_cotiredSources) + list (APPEND _allCotiredSourceFiles ${_cotiredSources}) + endif() + endforeach() + set (_targetMsgLevel STATUS) + if (NOT _targetLanguages) + string (REPLACE ";" " or " _languagesStr "${_languages}") + set (_disableMsg "No ${_languagesStr} source files.") + set (_targetUsePCH FALSE) + set (_targetAddSCU FALSE) + endif() + if (_targetUsePCH) + list (LENGTH _allSourceFiles _numberOfSources) + if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) + set (_disableMsg "Too few applicable sources.") + set (_targetUsePCH FALSE) + elseif (_allCotiredSourceFiles) + cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles}) + list (REMOVE_DUPLICATES _cotireTargets) + string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}") + set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.") + set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},") + set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.") + set (_targetMsgLevel SEND_ERROR) + set (_targetUsePCH FALSE) + elseif (XCODE AND _allExcludedSourceFiles) + # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target + set (_disableMsg "Exclusion of source files not supported for generator Xcode.") + set (_targetUsePCH FALSE) + elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY") + # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target + set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.") + set (_targetUsePCH FALSE) + endif() + endif() + set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH}) + set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU}) + cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles}) + if (_targetMsg) + if (NOT DEFINED COTIREMSG_${_target}) + set (COTIREMSG_${_target} "") + endif() + if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR + NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}") + # cache message to avoid redundant messages on re-configure + set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.") + message (${_targetMsgLevel} "${_targetMsg}") + endif() + endif() + set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE) +endfunction() + +function (cotire_compute_unity_max_number_of_includes _target _maxIncludesVar) + set (_sourceFiles ${ARGN}) + get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) + if (_maxIncludes MATCHES "(-j|--parallel|--jobs) ?([0-9]*)") + set (_numberOfThreads "${CMAKE_MATCH_2}") + if (NOT _numberOfThreads) + # use all available cores + ProcessorCount(_numberOfThreads) + endif() + list (LENGTH _sourceFiles _numberOfSources) + math (EXPR _maxIncludes "(${_numberOfSources} + ${_numberOfThreads} - 1) / ${_numberOfThreads}") + # a unity source segment must not contain less than COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES files + if (_maxIncludes LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) + set (_maxIncludes ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) + endif() + elseif (NOT _maxIncludes MATCHES "[0-9]+") + set (_maxIncludes 0) + endif() + if (COTIRE_DEBUG) + message (STATUS "${_target} unity source max includes = ${_maxIncludes}") + endif() + set (${_maxIncludesVar} ${_maxIncludes} PARENT_SCOPE) +endfunction() + +function (cotire_process_target_language _language _configurations _targetSourceDir _targetBinaryDir _target _wholeTargetVar _cmdsVar) + set (${_cmdsVar} "" PARENT_SCOPE) + get_target_property(_targetSourceFiles ${_target} SOURCES) + set (_sourceFiles "") + set (_excludedSources "") + set (_cotiredSources "") + cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) + if (NOT _sourceFiles AND NOT _cotiredSources) + return() + endif() + set (_wholeTarget ${${_wholeTargetVar}}) + set (_cmds "") + # check for user provided unity source file list + get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT) + if (NOT _unitySourceFiles) + set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources}) + endif() + cotire_generate_target_script( + ${_language} "${_configurations}" "${_targetSourceDir}" "${_targetBinaryDir}" ${_target} _targetScript ${_unitySourceFiles}) + cotire_compute_unity_max_number_of_includes(${_target} _maxIncludes ${_unitySourceFiles}) + cotire_make_unity_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles}) + if (NOT _unityFiles) + return() + endif() + cotire_setup_unity_generation_commands( + ${_language} "${_targetSourceDir}" ${_target} "${_targetScript}" "${_unityFiles}" _cmds ${_unitySourceFiles}) + cotire_make_prefix_file_path(${_language} ${_target} _prefixFile) + if (_prefixFile) + # check for user provided prefix header files + get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT) + if (_prefixHeaderFiles) + cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" _cmds ${_prefixHeaderFiles}) + else() + cotire_setup_multi_prefix_generation_command( + ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_unityFiles}" _cmds ${_unitySourceFiles}) + endif() + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + if (_targetUsePCH) + cotire_make_pch_file_path(${_language} "${_targetSourceDir}" ${_target} _pchFile) + if (_pchFile) + cotire_setup_pch_file_compilation( + ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) + if (_excludedSources) + set (_wholeTarget FALSE) + endif() + cotire_setup_pch_file_inclusion( + ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) + endif() + elseif (_prefixHeaderFiles) + # user provided prefix header must be included + cotire_setup_prefix_file_inclusion( + ${_language} ${_target} "${_prefixFile}" ${_sourceFiles}) + endif() + endif() + # mark target as cotired for language + set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}") + if (_prefixFile) + set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}") + if (_targetUsePCH AND _pchFile) + set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}") + endif() + endif() + set (${_wholeTargetVar} ${_wholeTarget} PARENT_SCOPE) + set (${_cmdsVar} ${_cmds} PARENT_SCOPE) +endfunction() + +function (cotire_setup_clean_target _target) + set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}") + if (NOT TARGET "${_cleanTargetName}") + cotire_set_cmd_to_prologue(_cmds) + get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE) + list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}") + add_custom_target(${_cleanTargetName} COMMAND ${_cmds} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + COMMENT "Cleaning up target ${_target} cotire generated files" VERBATIM) + cotire_init_target("${_cleanTargetName}") + endif() +endfunction() + +function (cotire_setup_pch_target _languages _configurations _target) + if ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") + # for makefile based generators, we add a custom target to trigger the generation of the cotire related files + set (_dependsFiles "") + foreach (_language ${_languages}) + set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE) + if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") + # Visual Studio and Intel only create precompiled header as a side effect + list (INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER) + endif() + cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props}) + if (_dependsFile) + list (APPEND _dependsFiles "${_dependsFile}") + endif() + endforeach() + if (_dependsFiles) + set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}") + add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles}) + cotire_init_target("${_pchTargetName}") + cotire_add_to_pch_all_target(${_pchTargetName}) + endif() + else() + # for other generators, we add the "clean all" target to clean up the precompiled header + cotire_setup_clean_all_target() + endif() +endfunction() + +function (cotire_setup_unity_build_target _languages _configurations _targetSourceDir _target) + get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME) + if (NOT _unityTargetName) + set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}") + endif() + # determine unity target sub type + get_target_property(_targetType ${_target} TYPE) + if ("${_targetType}" STREQUAL "EXECUTABLE") + get_target_property(_isWin32 ${_target} WIN32_EXECUTABLE) + get_target_property(_isMacOSX_Bundle ${_target} MACOSX_BUNDLE) + if (_isWin32) + set (_unityTargetSubType WIN32) + elseif (_isMacOSX_Bundle) + set (_unityTargetSubType MACOSX_BUNDLE) + else() + set (_unityTargetSubType "") + endif() + elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") + set (_unityTargetSubType "${CMAKE_MATCH_1}") + else() + message (WARNING "Unknown target type ${_targetType}.") + return() + endif() + # determine unity target sources + get_target_property(_targetSourceFiles ${_target} SOURCES) + set (_unityTargetSources ${_targetSourceFiles}) + foreach (_language ${_languages}) + get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE) + if (_unityFiles) + # remove source files that are included in the unity source + set (_sourceFiles "") + set (_excludedSources "") + set (_cotiredSources "") + cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) + if (_sourceFiles OR _cotiredSources) + list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources}) + endif() + # if cotire is applied to a target which has not been added in the current source dir, + # non-existing files cannot be referenced from the unity build target (this is a CMake restriction) + if (NOT "${_targetSourceDir}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + set (_nonExistingFiles "") + foreach (_file ${_unityTargetSources}) + if (NOT EXISTS "${_file}") + list (APPEND _nonExistingFiles "${_file}") + endif() + endforeach() + if (_nonExistingFiles) + if (COTIRE_VERBOSE) + message (STATUS "removing non-existing ${_nonExistingFiles} from ${_unityTargetName}") + endif() + list (REMOVE_ITEM _unityTargetSources ${_nonExistingFiles}) + endif() + endif() + # add unity source files instead + list (APPEND _unityTargetSources ${_unityFiles}) + endif() + endforeach() + if (COTIRE_DEBUG) + message (STATUS "add ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}") + endif() + # generate unity target + if ("${_targetType}" STREQUAL "EXECUTABLE") + add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) + else() + add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) + endif() + set (_outputDirProperties + ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_<CONFIG> + LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_<CONFIG> + RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_<CONFIG>) + # copy output location properties + if (COTIRE_UNITY_OUTPUT_DIRECTORY) + set (_setDefaultOutputDir TRUE) + if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}") + set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}") + else() + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) + cotrie_resolve_config_properites("${_configurations}" _properties ${_outputDirProperties}) + foreach (_property ${_properties}) + get_property(_outputDir TARGET ${_target} PROPERTY ${_property}) + if (_outputDir) + get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) + set_property(TARGET ${_unityTargetName} PROPERTY ${_property} "${_outputDir}") + set (_setDefaultOutputDir FALSE) + endif() + endforeach() + if (_setDefaultOutputDir) + get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) + endif() + endif() + if (_setDefaultOutputDir) + set_target_properties(${_unityTargetName} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}" + LIBRARY_OUTPUT_DIRECTORY "${_outputDir}" + RUNTIME_OUTPUT_DIRECTORY "${_outputDir}") + endif() + else() + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) + endif() + # copy output name + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} + ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_<CONFIG> + LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_<CONFIG> + OUTPUT_NAME OUTPUT_NAME_<CONFIG> + RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_<CONFIG> + PREFIX <CONFIG>_POSTFIX SUFFIX) + # copy compile stuff + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} + COMPILE_DEFINITIONS COMPILE_DEFINITIONS_<CONFIG> + COMPILE_FLAGS Fortran_FORMAT + INCLUDE_DIRECTORIES + INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_<CONFIG> + POSITION_INDEPENDENT_CODE) + # copy link stuff + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} + BUILD_WITH_INSTALL_RPATH INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH + LINKER_LANGUAGE LINK_DEPENDS LINK_DEPENDS_NO_SHARED + LINK_FLAGS LINK_FLAGS_<CONFIG> + LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_<CONFIG> + LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_<CONFIG> + LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC + STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_<CONFIG> + NO_SONAME SOVERSION VERSION) + # copy Qt stuff + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} + AUTOMOC AUTOMOC_MOC_OPTIONS) + # copy cmake stuff + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} + IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK) + # copy platform stuff + if (APPLE) + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} + BUNDLE BUNDLE_EXTENSION FRAMEWORK INSTALL_NAME_DIR MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST + OSX_ARCHITECTURES OSX_ARCHITECTURES_<CONFIG> PRIVATE_HEADER PUBLIC_HEADER RESOURCE) + elseif (WIN32) + cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} + GNUtoMS + PDB_NAME PDB_NAME_<CONFIG> PDB_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY_<CONFIG> + VS_DOTNET_REFERENCES VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_KEYWORD + VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER + VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES) + endif() + # use output name from original target + get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME) + if (NOT _targetOutputName) + set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}") + endif() + # use export symbol from original target + cotire_get_target_export_symbol("${_target}" _defineSymbol) + if (_defineSymbol) + set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}") + if ("${_targetType}" STREQUAL "EXECUTABLE") + set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE) + endif() + endif() + cotire_init_target(${_unityTargetName}) + cotire_add_to_unity_all_target(${_unityTargetName}) + set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}") +endfunction(cotire_setup_unity_build_target) + +function (cotire_target _target) + set(_options "") + set(_oneValueArgs SOURCE_DIR BINARY_DIR) + set(_multiValueArgs LANGUAGES CONFIGURATIONS) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + if (NOT _option_SOURCE_DIR) + set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + if (NOT _option_BINARY_DIR) + set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") + endif() + if (NOT _option_LANGUAGES) + get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + endif() + if (NOT _option_CONFIGURATIONS) + if (CMAKE_CONFIGURATION_TYPES) + set (_option_CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES}) + elseif (CMAKE_BUILD_TYPE) + set (_option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}") + else() + set (_option_CONFIGURATIONS "None") + endif() + endif() + # trivial checks + get_target_property(_imported ${_target} IMPORTED) + if (_imported) + message (WARNING "Imported target ${_target} cannot be cotired.") + return() + endif() + # check if target needs to be cotired for build type + # when using configuration types, the test is performed at build time + cotire_init_cotire_target_properties(${_target}) + if (NOT CMAKE_CONFIGURATION_TYPES) + if (CMAKE_BUILD_TYPE) + list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index) + else() + list (FIND _option_CONFIGURATIONS "None" _index) + endif() + if (_index EQUAL -1) + if (COTIRE_DEBUG) + message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})") + endif() + return() + endif() + endif() + # choose languages that apply to the target + cotire_choose_target_languages("${_option_SOURCE_DIR}" "${_target}" _targetLanguages ${_option_LANGUAGES}) + if (NOT _targetLanguages) + return() + endif() + list (LENGTH _targetLanguages _numberOfLanguages) + if (_numberOfLanguages GREATER 1) + set (_wholeTarget FALSE) + else() + set (_wholeTarget TRUE) + endif() + set (_cmds "") + foreach (_language ${_targetLanguages}) + cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}" + "${_option_SOURCE_DIR}" "${_option_BINARY_DIR}" ${_target} _wholeTarget _cmd) + if (_cmd) + list (APPEND _cmds ${_cmd}) + endif() + endforeach() + get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) + if (_targetAddSCU) + cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" "${_option_SOURCE_DIR}" ${_target}) + endif() + get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) + if (_targetUsePCH) + cotire_setup_target_pch_usage("${_targetLanguages}" "${_option_SOURCE_DIR}" ${_target} ${_wholeTarget} ${_cmds}) + cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target}) + endif() + get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN) + if (_targetAddCleanTarget) + cotire_setup_clean_target(${_target}) + endif() +endfunction() + +function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName) + if (_targetName) + file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*") + else() + file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*") + endif() + # filter files in intermediate directory + set (_filesToRemove "") + foreach (_file ${_cotireFiles}) + get_filename_component(_dir "${_file}" PATH) + get_filename_component(_dirName "${_dir}" NAME) + if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}") + list (APPEND _filesToRemove "${_file}") + endif() + endforeach() + if (_filesToRemove) + if (COTIRE_VERBOSE) + message (STATUS "removing ${_filesToRemove}") + endif() + file (REMOVE ${_filesToRemove}) + endif() +endfunction() + +function (cotire_init_target _targetName) + if (COTIRE_TARGETS_FOLDER) + set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}") + endif() + if (MSVC_IDE) + set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE) + endif() +endfunction() + +function (cotire_add_to_pch_all_target _pchTargetName) + set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}") + if (NOT TARGET "${_targetName}") + add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) + cotire_init_target("${_targetName}") + endif() + cotire_setup_clean_all_target() + add_dependencies(${_targetName} ${_pchTargetName}) +endfunction() + +function (cotire_add_to_unity_all_target _unityTargetName) + set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}") + if (NOT TARGET "${_targetName}") + add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) + cotire_init_target("${_targetName}") + endif() + cotire_setup_clean_all_target() + add_dependencies(${_targetName} ${_unityTargetName}) +endfunction() + +function (cotire_setup_clean_all_target) + set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}") + if (NOT TARGET "${_targetName}") + cotire_set_cmd_to_prologue(_cmds) + list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}") + add_custom_target(${_targetName} COMMAND ${_cmds} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "Cleaning up all cotire generated files" VERBATIM) + cotire_init_target("${_targetName}") + endif() +endfunction() + +function (cotire) + set(_options "") + set(_oneValueArgs SOURCE_DIR BINARY_DIR) + set(_multiValueArgs LANGUAGES CONFIGURATIONS) + cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) + set (_targets ${_option_UNPARSED_ARGUMENTS}) + if (NOT _option_SOURCE_DIR) + set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + if (NOT _option_BINARY_DIR) + set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") + endif() + foreach (_target ${_targets}) + if (TARGET ${_target}) + cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS} + SOURCE_DIR "${_option_SOURCE_DIR}" BINARY_DIR "${_option_BINARY_DIR}") + else() + message (WARNING "${_target} is not a target") + endif() + endforeach() +endfunction() + +if (CMAKE_SCRIPT_MODE_FILE) + + # cotire is being run in script mode + # locate -P on command args + set (COTIRE_ARGC -1) + foreach (_index RANGE ${CMAKE_ARGC}) + if (COTIRE_ARGC GREATER -1) + set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}") + math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1") + elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P") + set (COTIRE_ARGC 0) + endif() + endforeach() + + # include target script if available + if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$") + # the included target scripts sets up additional variables relating to the target (e.g., COTIRE_TARGET_SOURCES) + include("${COTIRE_ARGV2}") + endif() + + if (COTIRE_DEBUG) + message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}") + endif() + + if (WIN32) + # for MSVC, compiler IDs may not always be set correctly + if (MSVC) + set (CMAKE_C_COMPILER_ID "MSVC") + set (CMAKE_CXX_COMPILER_ID "MSVC") + endif() + endif() + + if (NOT COTIRE_BUILD_TYPE) + set (COTIRE_BUILD_TYPE "None") + endif() + string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig) + set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}}) + set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}}) + set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}}) + # check if target has been cotired for actual build type COTIRE_BUILD_TYPE + list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index) + if (_index GREATER -1) + set (_sources ${COTIRE_TARGET_SOURCES}) + set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}}) + else() + if (COTIRE_DEBUG) + message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})") + endif() + set (_sources "") + set (_sourcesDefinitions "") + endif() + set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS}) + set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS}) + set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS}) + set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS}) + + if ("${COTIRE_ARGV1}" STREQUAL "unity") + + cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources}) + cotire_generate_unity_source( + "${COTIRE_ARGV3}" ${_sources} + LANGUAGE "${COTIRE_TARGET_LANGUAGE}" + DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV2}" + SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions} + PRE_UNDEFS ${_targetPreUndefs} + POST_UNDEFS ${_targetPostUndefs} + SOURCES_PRE_UNDEFS ${_sourcesPreUndefs} + SOURCES_POST_UNDEFS ${_sourcesPostUndefs}) + + elseif ("${COTIRE_ARGV1}" STREQUAL "prefix") + + set (_files "") + foreach (_index RANGE 4 ${COTIRE_ARGC}) + if (COTIRE_ARGV${_index}) + list (APPEND _files "${COTIRE_ARGV${_index}}") + endif() + endforeach() + + cotire_generate_prefix_header( + "${COTIRE_ARGV3}" ${_files} + COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" + COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} + COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" + COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" + LANGUAGE "${COTIRE_TARGET_LANGUAGE}" + DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS} + IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}" + INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH} + IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}" + INCLUDE_DIRECTORIES ${_includeDirs} + COMPILE_DEFINITIONS ${_compileDefinitions} + COMPILE_FLAGS ${_compileFlags}) + + elseif ("${COTIRE_ARGV1}" STREQUAL "precompile") + + set (_files "") + foreach (_index RANGE 5 ${COTIRE_ARGC}) + if (COTIRE_ARGV${_index}) + list (APPEND _files "${COTIRE_ARGV${_index}}") + endif() + endforeach() + + cotire_precompile_prefix_header( + "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}" + COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" + COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} + COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" + COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" + LANGUAGE "${COTIRE_TARGET_LANGUAGE}" + INCLUDE_DIRECTORIES ${_includeDirs} + COMPILE_DEFINITIONS ${_compileDefinitions} + COMPILE_FLAGS ${_compileFlags}) + + elseif ("${COTIRE_ARGV1}" STREQUAL "combine") + + if (COTIRE_TARGET_LANGUAGE) + set (_startIndex 3) + else() + set (_startIndex 2) + endif() + set (_files "") + foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC}) + if (COTIRE_ARGV${_index}) + list (APPEND _files "${COTIRE_ARGV${_index}}") + endif() + endforeach() + if (COTIRE_TARGET_LANGUAGE) + cotire_generate_unity_source(${_files} LANGUAGE "${COTIRE_TARGET_LANGUAGE}") + else() + cotire_generate_unity_source(${_files}) + endif() + + elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup") + + cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}") + + else() + message (FATAL_ERROR "Unknown cotire command \"${COTIRE_ARGV1}\".") + endif() + +else() + + # cotire is being run in include mode + # set up all variable and property definitions + + unset (COTIRE_C_COMPILER_VERSION CACHE) + unset (COTIRE_CXX_COMPILER_VERSION CACHE) + + if (NOT DEFINED COTIRE_DEBUG_INIT) + if (DEFINED COTIRE_DEBUG) + set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG}) + else() + set (COTIRE_DEBUG_INIT FALSE) + endif() + endif() + option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT}) + + if (NOT DEFINED COTIRE_VERBOSE_INIT) + if (DEFINED COTIRE_VERBOSE) + set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE}) + else() + set (COTIRE_VERBOSE_INIT FALSE) + endif() + endif() + option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT}) + + set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING + "Ignore headers with the listed file extensions from the generated prefix header.") + + set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING + "Ignore headers from these directories when generating the prefix header.") + + set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING + "Ignore sources with the listed file extensions from the generated unity source.") + + set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "3" CACHE STRING + "Minimum number of sources in target required to enable use of precompiled header.") + + if (NOT DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT) + if (DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT ${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}) + elseif ("${CMAKE_GENERATOR}" MATCHES "JOM|Ninja|Visual Studio") + # enable parallelization for generators that run multiple jobs by default + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "-j") + else() + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "0") + endif() + endif() + set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT}" CACHE STRING + "Maximum number of source files to include in a single unity source file.") + + if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX) + set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix") + endif() + if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX) + set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity") + endif() + if (NOT COTIRE_INTDIR) + set (COTIRE_INTDIR "cotire") + endif() + if (NOT COTIRE_PCH_ALL_TARGET_NAME) + set (COTIRE_PCH_ALL_TARGET_NAME "all_pch") + endif() + if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME) + set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity") + endif() + if (NOT COTIRE_CLEAN_ALL_TARGET_NAME) + set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire") + endif() + if (NOT COTIRE_CLEAN_TARGET_SUFFIX) + set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire") + endif() + if (NOT COTIRE_PCH_TARGET_SUFFIX) + set (COTIRE_PCH_TARGET_SUFFIX "_pch") + endif() + if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX) + set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity") + endif() + if (NOT DEFINED COTIRE_TARGETS_FOLDER) + set (COTIRE_TARGETS_FOLDER "cotire") + endif() + if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY) + if ("${CMAKE_GENERATOR}" MATCHES "Ninja") + # generated Ninja build files do not work if the unity target produces the same output file as the cotired target + set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity") + else() + set (COTIRE_UNITY_OUTPUT_DIRECTORY "") + endif() + endif() + + # define cotire cache variables + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH" + BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." + FULL_DOCS + "The variable can be set to a semicolon separated list of include directories." + "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." + "If not defined, defaults to empty list." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS" + BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header." + FULL_DOCS + "The variable can be set to a semicolon separated list of file extensions." + "If a header file extension matches one in the list, it will be excluded from the generated prefix header." + "Includes with an extension in CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS are always ignored." + "If not defined, defaults to inc;inl;ipp." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS" + BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source." + FULL_DOCS + "The variable can be set to a semicolon separated list of file extensions." + "If a source file extension matches one in the list, it will be excluded from the generated unity source file." + "Source files with an extension in CMAKE_<LANG>_IGNORE_EXTENSIONS are always excluded." + "If not defined, defaults to m;mm." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES" + BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header." + FULL_DOCS + "The variable can be set to an integer > 0." + "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target." + "If not defined, defaults to 3." + ) + + define_property( + CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES" + BRIEF_DOCS "Maximum number of source files to include in a single unity source file." + FULL_DOCS + "This may be set to an integer >= 0." + "If 0, cotire will only create a single unity source file." + "If a target contains more than that number of source files, cotire will create multiple unity source files for it." + "Can be set to \"-j\" to optimize the count of unity source files for the number of available processor cores." + "Can be set to \"-j jobs\" to optimize the number of unity source files for the given number of simultaneous jobs." + "Is used to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." + "Defaults to \"-j\" for the generators Visual Studio, JOM or Ninja. Defaults to 0 otherwise." + ) + + # define cotire directory properties + + define_property( + DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" + BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header." + FULL_DOCS + "See target property COTIRE_ENABLE_PRECOMPILED_HEADER." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD" + BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory." + FULL_DOCS + "See target property COTIRE_ADD_UNITY_BUILD." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_ADD_CLEAN" + BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory." + FULL_DOCS + "See target property COTIRE_ADD_CLEAN." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" + BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." + FULL_DOCS + "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" + BRIEF_DOCS "Honor headers from these directories when generating the prefix header." + FULL_DOCS + "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file." + FULL_DOCS + "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file." + FULL_DOCS + "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS." + ) + + define_property( + DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" + BRIEF_DOCS "Maximum number of source files to include in a single unity source file." + FULL_DOCS + "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." + ) + + # define cotire target properties + + define_property( + TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED + BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header." + FULL_DOCS + "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header." + "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target." + "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header." + "The target name will be set to this target's name with the suffix _pch appended." + "Inherited from directory." + "Defaults to TRUE." + ) + + define_property( + TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED + BRIEF_DOCS "Add a new target that performs a unity build for this target." + FULL_DOCS + "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources." + "Most of the relevant target properties will be copied from this target to the new unity build target." + "Target dependencies and linked libraries have to be manually set up for the new unity build target." + "The unity target name will be set to this target's name with the suffix _unity appended." + "Inherited from directory." + "Defaults to TRUE." + ) + + define_property( + TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED + BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target." + FULL_DOCS + "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)." + "The clean target name will be set to this target's name with the suffix _clean_cotire appended." + "Inherited from directory." + "Defaults to FALSE." + ) + + define_property( + TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED + BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." + FULL_DOCS + "The property can be set to a list of directories." + "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." + "Inherited from directory." + "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}." + ) + + define_property( + TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED + BRIEF_DOCS "Honor headers from these directories when generating the prefix header." + FULL_DOCS + "The property can be set to a list of directories." + "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header." + "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH," + "the option which yields the closer relative path match wins." + "Inherited from directory." + "If not set, this property is initialized to the empty list." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file before each target source file." + "Inherited from directory." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file after each target source file." + "Inherited from directory." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED + BRIEF_DOCS "Maximum number of source files to include in a single unity source file." + FULL_DOCS + "This may be set to an integer > 0." + "If a target contains more than that number of source files, cotire will create multiple unity build files for it." + "If not set, cotire will only create a single unity source file." + "Inherited from directory." + "Defaults to empty." + ) + + define_property( + TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE_INIT" + BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one." + FULL_DOCS + "If set, cotire will only add the given file(s) to the generated unity source file." + "If not set, cotire will add all the target source files to the generated unity source file." + "The property can be set to a user provided unity source file." + "Defaults to empty." + ) + + define_property( + TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER_INIT" + BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one." + FULL_DOCS + "If set, cotire will add the given header file(s) to the generated prefix header file." + "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file." + "The property can be set to a user provided prefix header file (e.g., stdafx.h)." + "Defaults to empty." + ) + + define_property( + TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE" + BRIEF_DOCS "Read-only property. The generated <LANG> unity source file(s)." + FULL_DOCS + "cotire sets this property to the path of the generated <LANG> single computation unit source file for the target." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER" + BRIEF_DOCS "Read-only property. The generated <LANG> prefix header file." + FULL_DOCS + "cotire sets this property to the full path of the generated <LANG> language prefix header for the target." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_<LANG>_PRECOMPILED_HEADER" + BRIEF_DOCS "Read-only property. The generated <LANG> precompiled header file." + FULL_DOCS + "cotire sets this property to the full path of the generated <LANG> language precompiled header binary for the target." + "Defaults to empty string." + ) + + define_property( + TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME" + BRIEF_DOCS "The name of the generated unity build target corresponding to this target." + FULL_DOCS + "This property can be set to the desired name of the unity target that will be created by cotire." + "If not set, the unity target name will be set to this target's name with the suffix _unity appended." + "After this target has been processed by cotire, the property is set to the actual name of the generated unity target." + "Defaults to empty string." + ) + + # define cotire source properties + + define_property( + SOURCE PROPERTY "COTIRE_EXCLUDED" + BRIEF_DOCS "Do not modify source file's build command." + FULL_DOCS + "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header." + "The source file will also be excluded from the generated unity source file." + "Source files that have their COMPILE_FLAGS property set will be excluded by default." + "Defaults to FALSE." + ) + + define_property( + SOURCE PROPERTY "COTIRE_DEPENDENCY" + BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file." + FULL_DOCS + "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file." + "If the file is modified, cotire will re-generate the prefix header source upon build." + "Defaults to FALSE." + ) + + define_property( + SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file before this file is included." + "Defaults to empty string." + ) + + define_property( + SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" + BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file." + FULL_DOCS + "This may be set to a semicolon-separated list of preprocessor symbols." + "cotire will add corresponding #undef directives to the generated unit source file after this file is included." + "Defaults to empty string." + ) + + define_property( + SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE" + BRIEF_DOCS "Start a new unity source file which includes this source file as the first one." + FULL_DOCS + "If this property is set to TRUE, cotire will complete the current unity file and start a new one." + "The new unity source file will include this source file as the first one." + "This property essentially works as a separator for unity source files." + "Defaults to FALSE." + ) + + define_property( + SOURCE PROPERTY "COTIRE_TARGET" + BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target." + FULL_DOCS + "cotire sets this property to the name of target, that the source file's build command has been altered for." + "Defaults to empty string." + ) + + message (STATUS "cotire ${COTIRE_CMAKE_MODULE_VERSION} loaded.") + +endif()
\ No newline at end of file diff --git a/contrib/src/getopt/getopt.c b/contrib/src/getopt/getopt.c new file mode 100644 index 0000000..6fcebc6 --- /dev/null +++ b/contrib/src/getopt/getopt.c @@ -0,0 +1,562 @@ +/* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdio.h> +#include <windows.h> + +#define REPLACE_GETOPT /* use this getopt as the system getopt(3) */ + +#ifdef REPLACE_GETOPT +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +#undef optreset /* see getopt.h */ +#define optreset __mingw_optreset +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ +#endif + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#ifndef __CYGWIN__ +#define __progname __argv[0] +#else +extern char __declspec(dllimport) *__progname; +#endif + +#ifdef __CYGWIN__ +static char EMSG[] = ""; +#else +#define EMSG "" +#endif + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +static void +_vwarnx(const char *fmt,va_list ap) +{ + (void)fprintf(stderr,"%s: ",__progname); + if (fmt != NULL) + (void)vfprintf(stderr,fmt,ap); + (void)fprintf(stderr,"\n"); +} + +static void +warnx(const char *fmt,...) +{ + va_list ap; + va_start(ap,fmt); + _vwarnx(fmt,ap); + va_end(ap); +} + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, ambiguous, match; + +#define IDENTICAL_INTERPRETATION(_x, _y) \ + (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ + long_options[(_x)].flag == long_options[(_y)].flag && \ + long_options[(_x)].val == long_options[(_y)].val) + + current_argv = place; + match = -1; + ambiguous = 0; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + ambiguous = 0; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else if (!IDENTICAL_INTERPRETATION(i, match)) + ambiguous = 1; + } + if (ambiguous) { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +#undef IDENTICAL_INTERPRETATION +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + * + * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or + * optreset != 0 for GNU compatibility. + */ + if (posixly_correct == -1 || optreset != 0) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +#ifdef REPLACE_GETOPT +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} +#endif /* REPLACE_GETOPT */ + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +}
\ No newline at end of file diff --git a/contrib/src/getopt/getopt.h b/contrib/src/getopt/getopt.h new file mode 100644 index 0000000..8d526ab --- /dev/null +++ b/contrib/src/getopt/getopt.h @@ -0,0 +1,95 @@ +#ifndef __GETOPT_H__ +/** + * DISCLAIMER + * This file has no copyright assigned and is placed in the Public Domain. + * This file is a part of the w64 mingw-runtime package. + * + * The w64 mingw-runtime package and its code is distributed in the hope that it + * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR + * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to + * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define __GETOPT_H__ + +/* All the headers include this file. */ +#include <crtdefs.h> + +#ifdef __cplusplus +extern "C" { +#endif + +extern int optind; /* index of first non-option in argv */ +extern int optopt; /* single option character, as parsed */ +extern int opterr; /* flag to enable built-in diagnostics... */ + /* (user may set to zero, to suppress) */ + +extern char *optarg; /* pointer to argument of current option */ + +extern int getopt(int nargc, char * const *nargv, const char *options); + +#ifdef _BSD_SOURCE +/* + * BSD adds the non-standard `optreset' feature, for reinitialisation + * of `getopt' parsing. We support this feature, for applications which + * proclaim their BSD heritage, before including this header; however, + * to maintain portability, developers are advised to avoid it. + */ +# define optreset __mingw_optreset +extern int optreset; +#endif +#ifdef __cplusplus +} +#endif +/* + * POSIX requires the `getopt' API to be specified in `unistd.h'; + * thus, `unistd.h' includes this header. However, we do not want + * to expose the `getopt_long' or `getopt_long_only' APIs, when + * included in this manner. Thus, close the standard __GETOPT_H__ + * declarations block, and open an additional __GETOPT_LONG_H__ + * specific block, only when *not* __UNISTD_H_SOURCED__, in which + * to declare the extended API. + */ +#endif /* !defined(__GETOPT_H__) */ + +#if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) +#define __GETOPT_LONG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct option /* specification for a long form option... */ +{ + const char *name; /* option name, without leading hyphens */ + int has_arg; /* does it take an argument? */ + int *flag; /* where to save its status, or NULL */ + int val; /* its associated status value */ +}; + +enum /* permitted values for its `has_arg' field... */ +{ + no_argument = 0, /* option never takes an argument */ + required_argument, /* option always requires an argument */ + optional_argument /* option may take an argument */ +}; + +extern int getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx); +extern int getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx); +/* + * Previous MinGW implementation had... + */ +#ifndef HAVE_DECL_GETOPT +/* + * ...for the long form API only; keep this for compatibility. + */ +# define HAVE_DECL_GETOPT 1 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */
\ No newline at end of file diff --git a/src/bindings/swig/php/uscxmlNativePHP.php b/src/bindings/swig/php/uscxmlNativePHP.php index 668e01b..dccfbaf 100644 --- a/src/bindings/swig/php/uscxmlNativePHP.php +++ b/src/bindings/swig/php/uscxmlNativePHP.php @@ -2,7 +2,7 @@ /* ---------------------------------------------------------------------------- * This file was automatically generated by SWIG (http://www.swig.org). - * Version 2.0.7 + * Version 2.0.9 * * This file is not intended to be easily readable and contains a number of * coding conventions designed to improve portability and efficiency. Do not make @@ -226,6 +226,42 @@ class Params { } } +class Blob { + public $_cPtr=null; + protected $_pData=array(); + + function __set($var,$value) { + $func = 'Blob_'.$var.'_set'; + if (function_exists($func)) return call_user_func($func,$this->_cPtr,$value); + if ($var === 'thisown') return swig_uscxmlNativePHP_alter_newobject($this->_cPtr,$value); + $this->_pData[$var] = $value; + } + + function __isset($var) { + if (function_exists('Blob_'.$var.'_set')) return true; + if ($var === 'thisown') return true; + return array_key_exists($var, $this->_pData); + } + + function __get($var) { + $func = 'Blob_'.$var.'_get'; + if (function_exists($func)) return call_user_func($func,$this->_cPtr); + if ($var === 'thisown') return swig_uscxmlNativePHP_get_newobject($this->_cPtr); + return $this->_pData[$var]; + } + + function __construct($size_or_data,$size=null,$adopt=false) { + if (is_resource($size_or_data) && get_resource_type($size_or_data) === '_p_uscxml__Blob') { + $this->_cPtr=$size_or_data; + return; + } + switch (func_num_args()) { + case 1: $this->_cPtr=new_Blob($size_or_data); break; + default: $this->_cPtr=new_Blob($size_or_data,$size,$adopt); + } + } +} + class Data { public $_cPtr=null; protected $_pData=array(); @@ -254,17 +290,16 @@ class Data { const INTERPRETED = Data_INTERPRETED; - const BINARY = Data_BINARY; - - function __construct($atom__or_dom=null,$type_=null) { - if (is_resource($atom__or_dom) && get_resource_type($atom__or_dom) === '_p_uscxml__Data') { - $this->_cPtr=$atom__or_dom; + function __construct($atom__or_data_or_dom=null,$type__or_size=null,$adopt=null) { + if (is_resource($atom__or_data_or_dom) && get_resource_type($atom__or_data_or_dom) === '_p_uscxml__Data') { + $this->_cPtr=$atom__or_data_or_dom; return; } switch (func_num_args()) { case 0: $this->_cPtr=new_Data(); break; - case 1: $this->_cPtr=new_Data($atom__or_dom); break; - default: $this->_cPtr=new_Data($atom__or_dom,$type_); + case 1: $this->_cPtr=new_Data($atom__or_data_or_dom); break; + case 2: $this->_cPtr=new_Data($atom__or_data_or_dom,$type__or_size); break; + default: $this->_cPtr=new_Data($atom__or_data_or_dom,$type__or_size,$adopt); } } @@ -359,7 +394,6 @@ class Event { } function __get($var) { - if ($var === 'namelist') return new StringMap(Event_namelist_get($this->_cPtr)); if ($var === 'data') return new Data(Event_data_get($this->_cPtr)); $func = 'Event_'.$var.'_get'; if (function_exists($func)) return call_user_func($func,$this->_cPtr); @@ -502,18 +536,16 @@ class Event { } function getNameList() { - $r=Event_getNameList($this->_cPtr); - if (is_resource($r)) { - $c=substr(get_resource_type($r), (strpos(get_resource_type($r), '__') ? strpos(get_resource_type($r), '__') + 2 : 3)); - if (class_exists($c)) return new $c($r); - return new StringMap($r); - } - return $r; + return Event_getNameList($this->_cPtr); } function getParams() { return Event_getParams($this->_cPtr); } + + static function getParam($params,$name,$target) { + return Event_getParam($params,$name,$target); + } } class InvokeRequest extends Event { @@ -980,10 +1012,6 @@ class Interpreter { return Interpreter_getProperAncestors($this->_cPtr,$s1,$s2); } - static function getUUID() { - return Interpreter_getUUID(); - } - function getImpl() { return Interpreter_getImpl($this->_cPtr); } diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index 7e61ed7..065b61e 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -1,3 +1,4 @@ +#include "uscxml/config.h" #include "uscxml/Common.h" #include "uscxml/Interpreter.h" #include "uscxml/URL.h" @@ -40,6 +41,145 @@ namespace uscxml { using namespace Arabica::XPath; using namespace Arabica::DOM; +void InterpreterOptions::printUsageAndExit(const char* progName) { + printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progName); + printf("Usage\n"); + printf("\t%s", progName); +#ifdef BUILD_AS_PLUGINS + printf(" [-e pluginPath]"); +#endif + printf("[-v] [-pN] URL\n"); + printf("\n"); + printf("Options\n"); + printf("\t-v : be verbose\n"); + printf("\t-pN : port for HTTP server\n"); + printf("\t-d : write each configuration as a dot file\n"); + printf("\n"); + exit(1); +} + +unsigned int InterpreterOptions::getCapabilities() { + unsigned int capabilities = CAN_NOTHING; + if (withHTTP) + capabilities = capabilities | CAN_GENERIC_HTTP | CAN_BASIC_HTTP; + + return capabilities; +} + +InterpreterOptions InterpreterOptions::fromCmdLine(int argc, char** argv) { + InterpreterOptions options; + struct option longOptions[] = { + {"verbose", no_argument, 0, 'v'}, + {"dot", no_argument, 0, 'd'}, + {"port", required_argument, 0, 't'}, + {"ssl-port", required_argument, 0, 's'}, + {"certificate", required_argument, 0, 'c'}, + {"private-key", required_argument, 0, 0}, + {"public-key", required_argument, 0, 0}, + {"plugin-path", required_argument, 0, 'p'}, + {"loglevel", required_argument, 0, 'l'}, + {"disable-http", no_argument, 0, 0}, + {0, 0, 0, 0} + }; + + opterr = 0; + if (argc < 2) { + options.error = "No SCXML document to evaluate"; + return options; + } + + InterpreterOptions* currOptions = &options; + + // parse global options + int optionInd = 0; + int option; + for (;;) { + option = getopt_long_only(argc, argv, "+vdt:s:c:p:l:", longOptions, &optionInd); + if (option == -1) { + if (optind == argc) + // we are done with parsing + goto DONE_PARSING_CMD; + + std::string url = argv[optind]; + options.interpreters[url] = new InterpreterOptions(); + currOptions = options.interpreters[url]; + + argc -= optind; + argv += optind; + optind = 0; + + if (argc <= 1) + goto DONE_PARSING_CMD; + + } + switch(option) { + // cases without short option + case 0: { + if (boost::equals(longOptions[optionInd].name, "disable-http")) { + currOptions->withHTTP = false; + } else if (boost::equals(longOptions[optionInd].name, "private-key")) { + currOptions->privateKey = optarg; + } else if (boost::equals(longOptions[optionInd].name, "public-key")) { + currOptions->publicKey = optarg; + } + break; + } + // cases with short-hand options + case 'l': + currOptions->logLevel = strTo<unsigned int>(optarg); + break; + case 'p': + currOptions->pluginPath = optarg; + break; + case 'd': + currOptions->useDot = true; + break; + case 'c': + currOptions->certificate = optarg; + break; + case 't': + currOptions->httpPort = strTo<unsigned short>(optarg); + break; + case 's': + currOptions->httpsPort = strTo<unsigned short>(optarg); + break; + case 'v': + currOptions->verbose = true; + break; + case '?': { + std::string param = argv[optind - 1]; + if (boost::starts_with(param, "--")) { + param = param.substr(2, param.length() - 2); + } else if (boost::starts_with(param, "-")) { + param = param.substr(1, param.length() - 1); + } else { + break; + } + boost::trim(param); + + size_t equalPos = param.find("="); + if (equalPos != std::string::npos) { + std::string key = param.substr(0, equalPos); + std::string value = param.substr(equalPos + 1, param.length() - (equalPos + 1)); + currOptions->additionalParameters[key] = value; + } else { + currOptions->additionalParameters[param] = ""; + } + break; + } + default: + break; + } + } + +DONE_PARSING_CMD: + + if (options.interpreters.size() == 0) + options.error = "No SCXML document to evaluate"; + + return options; +} + std::map<std::string, boost::weak_ptr<InterpreterImpl> > Interpreter::_instances; tthread::recursive_mutex Interpreter::_instanceMutex; @@ -1761,26 +1901,15 @@ IOProcessor InterpreterImpl::getIOProcessor(const std::string& type) { return _ioProcessors[type]; } -void InterpreterImpl::setCmdLineOptions(int argc, char** argv) { - char* key = NULL; - char* value = NULL; - for (int i = 0; i < argc; i++) { - if (false) { - } else if (strlen(argv[i]) > 2 && strncmp(&argv[i][0], "-", 1) == 0 && strncmp(&argv[i][1], "-", 1) == 0) { - // longopt - key = &argv[i][2]; - } else if (strlen(argv[i]) > 1 && strncmp(&argv[i][0], "-", 1) == 0 && strncmp(&argv[i][1], "-", 1) != 0) { - // shortopt - key = &argv[i][1]; - } - if (key != NULL) { - if (i + 1 < argc && strncmp(&argv[i + 1][0], "-", 1) != 0) { - value = argv[++i]; - _cmdLineOptions.compound[key] = Data(value, Data::VERBATIM); - } else { - _cmdLineOptions.compound[key] = Data("true"); - } +void InterpreterImpl::setCmdLineOptions(std::map<std::string, std::string> params) { + std::map<std::string, std::string>::iterator paramIter = params.begin(); + while (paramIter != params.end()) { + if (paramIter->second.length() > 0) { + _cmdLineOptions.compound[paramIter->first] = Data(paramIter->second, Data::VERBATIM); + } else { + _cmdLineOptions.compound[paramIter->first] = Data("true"); } + paramIter++; } } diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index aadef72..b8093ab 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -1,7 +1,10 @@ #ifndef RUNTIME_H_SQ1MBKGN #define RUNTIME_H_SQ1MBKGN +// this has to be the first include or MSVC will run amok #include "uscxml/Common.h" +#include "getopt.h" + #include "uscxml/URL.h" #include <boost/algorithm/string.hpp> @@ -61,6 +64,44 @@ enum Capabilities { CAN_GENERIC_HTTP = 2, }; +class InterpreterOptions { +public: + bool useDot; + bool verbose; + bool withHTTP; + bool withHTTPS; + int logLevel; + unsigned short httpPort; + unsigned short httpsPort; + std::string pluginPath; + std::string certificate; + std::string privateKey; + std::string publicKey; + std::map<std::string, InterpreterOptions*> interpreters; + std::map<std::string, std::string> additionalParameters; + + std::string error; + + operator bool() { + return error.length() == 0; + } + + static void printUsageAndExit(const char* progName); + static InterpreterOptions fromCmdLine(int argc, char** argv); + unsigned int getCapabilities(); + +protected: + InterpreterOptions() : + useDot(false), + verbose(false), + withHTTP(true), + withHTTPS(true), + logLevel(0), + httpPort(8080), + httpsPort(8443) + {} +}; + class InterpreterImpl : public boost::enable_shared_from_this<InterpreterImpl> { public: @@ -100,7 +141,7 @@ public: return _baseURI; } - void setCmdLineOptions(int argc, char** argv); + void setCmdLineOptions(std::map<std::string, std::string> params); Data getCmdLineOptions() { return _cmdLineOptions; } @@ -400,8 +441,8 @@ public: return _impl->getNameSpaceInfo(); } - void setCmdLineOptions(int argc, char** argv) { - return _impl->setCmdLineOptions(argc, argv); + void setCmdLineOptions(std::map<std::string, std::string> params) { + return _impl->setCmdLineOptions(params); } Data getCmdLineOptions() { return _impl->getCmdLineOptions(); diff --git a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp index cbb82eb..fe16361 100644 --- a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp +++ b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.cpp @@ -26,14 +26,12 @@ DelayedEventQueue::~DelayedEventQueue() { } void DelayedEventQueue::run(void* instance) { - DelayedEventQueue* THIS = (DelayedEventQueue*)instance; + DelayedEventQueue* INSTANCE = (DelayedEventQueue*)instance; int result; - while(THIS->_isStarted) { - { - //result = event_base_dispatch(THIS->_eventLoop); - result = event_base_loop(THIS->_eventLoop, EVLOOP_NO_EXIT_ON_EMPTY); - (void)result; - } + while(INSTANCE->_isStarted) { + //result = event_base_dispatch(THIS->_eventLoop); + result = event_base_loop(INSTANCE->_eventLoop, EVLOOP_NO_EXIT_ON_EMPTY); + (void)result; } } diff --git a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h index 0b72719..fa76c3f 100644 --- a/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h +++ b/src/uscxml/concurrency/eventqueue/DelayedEventQueue.h @@ -19,9 +19,9 @@ class DelayedEventQueue { public: enum OpMask { - FD_READ = EV_READ, - FD_WRITE = EV_WRITE, - FD_SIGNAL = EV_SIGNAL + DEQ_READ = EV_READ, + DEQ_WRITE = EV_WRITE, + DEQ_SIGNAL = EV_SIGNAL }; struct callbackData { diff --git a/src/uscxml/pch.h b/src/uscxml/pch.h new file mode 100644 index 0000000..e4ae2d1 --- /dev/null +++ b/src/uscxml/pch.h @@ -0,0 +1,14 @@ +#include "uscxml/Common.h" + +#include <boost/algorithm/string.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/lexical_cast.hpp> + +#include <iostream> +#include <set> +#include <map> +#include <list> +#include <vector> +#include <string> + +#include <DOM/Document.hpp>
\ No newline at end of file diff --git a/src/uscxml/plugins/datamodel/CMakeLists.txt b/src/uscxml/plugins/datamodel/CMakeLists.txt index 5fe82b7..3b6a852 100644 --- a/src/uscxml/plugins/datamodel/CMakeLists.txt +++ b/src/uscxml/plugins/datamodel/CMakeLists.txt @@ -1,63 +1,65 @@ -if (JSC_FOUND AND BUILD_DM_ECMA) - set(USCXML_DATAMODELS "ecmascript(JSC) ${USCXML_DATAMODELS}") - # JavaScriptCore ecmascript datamodel - file(GLOB JSC_DATAMODEL - ecmascript/JavaScriptCore/*.cpp - ecmascript/JavaScriptCore/*.h - ecmascript/*.cpp - ecmascript/*.h - ) - source_group("Datamodel\\jsc" FILES ${JSC_DATAMODEL}) - file(GLOB_RECURSE JSC_DOM - ecmascript/JavaScriptCore/dom/*.cpp - ecmascript/JavaScriptCore/dom/*.h - ) - source_group("Datamodel\\DOM" FILES ${JSC_DOM}) - if (BUILD_AS_PLUGINS) - add_library( - datamodel_jsc SHARED - ${JSC_DATAMODEL} - ${JSC_DOM}) - target_link_libraries(datamodel_jsc - uscxml - ${JSC_LIBRARY}) - set_target_properties(datamodel_jsc PROPERTIES FOLDER "Plugin DataModel") - else() - list (APPEND USCXML_FILES ${JSC_DATAMODEL}) - list (APPEND USCXML_FILES ${JSC_DOM}) - list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY}) - endif() -else() - -# GOOGLE V8 ecmascript datamodel - set(USCXML_DATAMODELS "ecmascript(V8) ${USCXML_DATAMODELS}") - # set(ENV{V8_SRC} ${CMAKE_SOURCE_DIR}/../v8) - if (V8_FOUND AND BUILD_DM_ECMA) - file(GLOB V8_DATAMODEL - ecmascript/v8/*.cpp - ecmascript/v8/*.h +if (BUILD_DM_ECMA) + if (JSC_FOUND) + set(USCXML_DATAMODELS "ecmascript(JSC) ${USCXML_DATAMODELS}") + # JavaScriptCore ecmascript datamodel + file(GLOB JSC_DATAMODEL + ecmascript/JavaScriptCore/*.cpp + ecmascript/JavaScriptCore/*.h ecmascript/*.cpp ecmascript/*.h ) - source_group("Datamodel\\v8" FILES ${V8_DATAMODEL}) - file(GLOB_RECURSE V8_DOM - ecmascript/v8/dom/*.cpp - ecmascript/v8/dom/*.h + source_group("Datamodel\\jsc" FILES ${JSC_DATAMODEL}) + file(GLOB_RECURSE JSC_DOM + ecmascript/JavaScriptCore/dom/*.cpp + ecmascript/JavaScriptCore/dom/*.h ) - source_group("Datamodel\\v8\\DOM" FILES ${V8_DOM}) - + source_group("Datamodel\\DOM" FILES ${JSC_DOM}) if (BUILD_AS_PLUGINS) add_library( - datamodel_v8 SHARED - ${V8_DATAMODEL} - ${V8_DOM}) - target_link_libraries(datamodel_v8 + datamodel_jsc SHARED + ${JSC_DATAMODEL} + ${JSC_DOM}) + target_link_libraries(datamodel_jsc uscxml - ${V8_LIBRARY}) - set_target_properties(datamodel_v8 PROPERTIES FOLDER "Plugin DataModel") + ${JSC_LIBRARY}) + set_target_properties(datamodel_jsc PROPERTIES FOLDER "Plugin DataModel") else() - list (APPEND USCXML_FILES ${V8_DATAMODEL}) - list (APPEND USCXML_FILES ${V8_DOM}) + list (APPEND USCXML_FILES ${JSC_DATAMODEL}) + list (APPEND USCXML_FILES ${JSC_DOM}) + list (APPEND USCXML_OPT_LIBS ${JSC_LIBRARY}) + endif() + else() + + # GOOGLE V8 ecmascript datamodel + set(USCXML_DATAMODELS "ecmascript(V8) ${USCXML_DATAMODELS}") + # set(ENV{V8_SRC} ${CMAKE_SOURCE_DIR}/../v8) + if (V8_FOUND AND BUILD_DM_ECMA) + file(GLOB V8_DATAMODEL + ecmascript/v8/*.cpp + ecmascript/v8/*.h + ecmascript/*.cpp + ecmascript/*.h + ) + source_group("Datamodel\\v8" FILES ${V8_DATAMODEL}) + file(GLOB_RECURSE V8_DOM + ecmascript/v8/dom/*.cpp + ecmascript/v8/dom/*.h + ) + source_group("Datamodel\\v8\\DOM" FILES ${V8_DOM}) + + if (BUILD_AS_PLUGINS) + add_library( + datamodel_v8 SHARED + ${V8_DATAMODEL} + ${V8_DOM}) + target_link_libraries(datamodel_v8 + uscxml + ${V8_LIBRARY}) + set_target_properties(datamodel_v8 PROPERTIES FOLDER "Plugin DataModel") + else() + list (APPEND USCXML_FILES ${V8_DATAMODEL}) + list (APPEND USCXML_FILES ${V8_DOM}) + endif() endif() endif() endif() diff --git a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h index 460afd4..e7d7e4c 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h +++ b/src/uscxml/plugins/datamodel/ecmascript/TypedArray.h @@ -1,6 +1,7 @@ #ifndef TYPEDARRAY_H_99815BLY #define TYPEDARRAY_H_99815BLY +#include "uscxml/Common.h" #include "uscxml/Message.h" #include <string> diff --git a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h index 43b98ce..f35b13f 100644 --- a/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h +++ b/src/uscxml/plugins/datamodel/ecmascript/v8/V8DOM.h @@ -1,6 +1,7 @@ #ifndef V8DOM_H_LKE1HKJK #define V8DOM_H_LKE1HKJK +#include "uscxml/Common.h" #include "uscxml/Interpreter.h" #include <v8.h> #include <XPath/XPath.hpp> diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.cpp b/src/uscxml/plugins/invoker/im/IMInvoker.cpp index cff9b5f..1e12650 100644 --- a/src/uscxml/plugins/invoker/im/IMInvoker.cpp +++ b/src/uscxml/plugins/invoker/im/IMInvoker.cpp @@ -7,12 +7,13 @@ #endif #define GET_INSTANCE_IN_CALLBACK(account) \ -tthread::lock_guard<tthread::mutex> lock(_accountMutex); \ +tthread::lock_guard<tthread::recursive_mutex> lock(_accountMutex); \ +IMInvoker* inst = NULL;\ if (_accountInstances.find(account) == _accountInstances.end()) { \ LOG(ERROR) << "Callback for unknown account called"; \ - return; \ -} \ -IMInvoker* inst = _accountInstances[account];\ +} else {\ + inst = _accountInstances[account];\ +} namespace uscxml { @@ -35,13 +36,19 @@ PurpleEventLoopUiOps IMInvoker::_uiEventLoopOps = purpleEventTimeoutRemove, purpleEventInputAdd, purpleEventInputRemove, - NULL, //purpleEventInputGetError, + purpleEventInputGetError, purpleEventTimeoutAddSec, NULL, NULL, NULL }; - + +PurpleDebugUiOps IMInvoker::_uiDebugOps = { + purpleDebugPrint, + purpleDebugIsEnabled, + NULL, NULL, NULL, NULL +}; + PurpleAccountUiOps IMInvoker::_uiAccountOps = { accountNotifyAdded, accountStatusChanged, @@ -181,7 +188,7 @@ PurpleWhiteboardUiOps IMInvoker::_uiWhiteboardOps = { }; PurpleCoreUiOps IMInvoker::_uiCoreOps = { - NULL, + purplePrefsInit, NULL, purpleUIInit, NULL, @@ -196,9 +203,39 @@ DelayedEventQueue* IMInvoker::_eventQueue = NULL; tthread::mutex IMInvoker::_initMutex; tthread::condition_variable IMInvoker::_initCond; -tthread::mutex IMInvoker::_accountMutex; +tthread::recursive_mutex IMInvoker::_accountMutex; std::map<PurpleAccount*, IMInvoker*> IMInvoker::_accountInstances; + +void IMInvoker::setupPurpleSignals() { + int handle; + // connection signals + purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, PURPLE_CALLBACK(signedOnCB), NULL); + + // conversation signals + purple_signal_connect(purple_conversations_get_handle(), "conversation-created", &handle, PURPLE_CALLBACK(conversationCreatedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "chat-joined", &handle, PURPLE_CALLBACK(chatJoinedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "chat-join-failed", &handle, PURPLE_CALLBACK(chatJoinFailedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "buddy-typing", &handle, PURPLE_CALLBACK(buddyTypingCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "buddy-typed", &handle, PURPLE_CALLBACK(buddyTypedCB), NULL); + purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped", &handle, PURPLE_CALLBACK(buddyTypingStoppedCB), NULL); + + // buddy signals + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_SIGNON)); + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_SIGNOFF)); + purple_signal_connect(purple_blist_get_handle(), "buddy-got-login-time", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_SIGNON_TIME)); + purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", &handle, PURPLE_CALLBACK(buddyIdleChangedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", &handle, PURPLE_CALLBACK(buddyStatusChangedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-icon-changed", &handle, PURPLE_CALLBACK(buddyEventCB), GINT_TO_POINTER(PURPLE_BUDDY_ICON)); + purple_signal_connect(purple_blist_get_handle(), "buddy-added", &handle, PURPLE_CALLBACK(buddyAddedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-removed", &handle, PURPLE_CALLBACK(buddyRemovedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "blist-node-aliased", &handle, PURPLE_CALLBACK(blistNodeAliasedCB), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-caps-changed", &handle, PURPLE_CALLBACK(buddyCapsChangedCB), NULL); + + // xfer signals + purple_signal_connect(purple_xfers_get_handle(), "file-recv-request", &handle, PURPLE_CALLBACK(fileRecvRequestCB), NULL); +} + void IMInvoker::initLibPurple(void *userdata, const std::string event) { _initMutex.lock(); @@ -218,7 +255,6 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) { purple_debug_set_enabled(false); purple_core_set_ui_ops(&_uiCoreOps); -// purple_eventloop_set_ui_ops(&glib_eventloops); purple_eventloop_set_ui_ops(&_uiEventLoopOps); purple_plugins_add_search_path("/usr/local/lib/purple-3"); @@ -257,64 +293,153 @@ void IMInvoker::initLibPurple(void *userdata, const std::string event) { _initMutex.unlock(); _initCond.notify_all(); +} +// purple event callbacks +void IMInvoker::signedOnCB(PurpleConnection *gc, gpointer null) { + PurpleAccount *account = purple_connection_get_account(gc); + GET_INSTANCE_IN_CALLBACK(account); + if (!inst) + return; + +#if 0 + GSList *buddies = purple_find_buddies(purple_connection_get_account(gc), NULL); + GSList *cur; + for (cur = buddies; cur; cur = cur->next) { + buddyAddedCB((PurpleBuddy *)cur->data); + } + g_slist_free(buddies); +#endif + + // set my status to active + PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); + purple_savedstatus_activate(status); + + Event retEv("im.signed.on"); + inst->returnEvent(retEv); } -void IMInvoker::buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *newstatus) { +void IMInvoker::conversationCreatedCB(PurpleConversation *conv, void *data) {} +void IMInvoker::chatJoinedCB(PurpleConversation *conv, void *data) {} +void IMInvoker::chatJoinFailedCB(PurpleConnection *gc, GHashTable *components) {} +void IMInvoker::buddyTypingCB(PurpleAccount *account, const char *name, void *data) {} +void IMInvoker::buddyTypedCB(PurpleAccount *account, const char *name, void *data) {} +void IMInvoker::buddyTypingStoppedCB(PurpleAccount *account, const char *name, void *data) {} + +void IMInvoker::buddyEventCB(PurpleBuddy *buddy, PurpleBuddyEvent event) { + if (!buddy) + return; + PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); + if (!inst) + return; + + switch (event) { + case PURPLE_BUDDY_SIGNOFF: + case PURPLE_BUDDY_SIGNON: { + PurplePresence* presence = purple_buddy_get_presence(buddy); + PurpleStatus* status = purple_presence_get_active_status(presence); + buddyStatusChangedCB(buddy, NULL, status, event); + break; + } + case PURPLE_BUDDY_ICON: + break; + + default: + break; + } - std::string buddyName = purple_buddy_get_name(buddy); - inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy); } -void IMInvoker::buddyIdleChangeCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle) { +void IMInvoker::buddyIdleChangedCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle, PurpleBuddyEvent event) { +} + +void IMInvoker::buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *newstatus, PurpleBuddyEvent event) { PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); -} + + std::string buddyName = purple_buddy_get_name(buddy); + Data buddyData = buddyToData(buddy); + inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyData; + + Event retEv("im.buddy.status.changed"); + retEv.data = buddyData; + inst->returnEvent(retEv); -void IMInvoker::buddyUpdateIdleCB() { } void IMInvoker::buddyAddedCB(PurpleBuddy* buddy) { PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); + if (!inst) + return; + + std::string buddyName = purple_buddy_get_name(buddy); + + Event retEv("im.buddy.added"); + retEv.data.compound["name"] = Data(buddyName, Data::VERBATIM); + inst->returnEvent(retEv); + + buddyStatusChangedCB(buddy, NULL, purple_presence_get_active_status(purple_buddy_get_presence(buddy)), PURPLE_BUDDY_NONE); + } void IMInvoker::buddyRemovedCB(PurpleBuddy* buddy) { PurpleAccount *account = purple_buddy_get_account(buddy); GET_INSTANCE_IN_CALLBACK(account); + std::string buddyName = purple_buddy_get_name(buddy); + + Event retEv("im.buddy.removed"); + retEv.data.compound["name"] = Data(buddyName, Data::VERBATIM); + inst->returnEvent(retEv); + + inst->_dataModelVars.compound["buddies"].compound.erase(buddyName); + } -void IMInvoker::buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps) { - PurpleAccount *account = purple_buddy_get_account(buddy); - GET_INSTANCE_IN_CALLBACK(account); +void IMInvoker::blistNodeAliasedCB(PurpleBlistNode *node, char *old_alias) { } -gboolean IMInvoker::jabberRcvdPresenceCB(PurpleConnection *gc, const char *type, const char *from, xmlnode *presence) { - PurpleAccount *account = purple_connection_get_account(gc); - GET_INSTANCE_IN_CALLBACK(account); +void IMInvoker::fileRecvRequestCB(PurpleXfer *xfer) { + purple_xfer_set_local_filename(xfer, ""); } -void IMInvoker::buddySignOnOffCB(PurpleBuddy *buddy) { + +void IMInvoker::buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps) { PurpleAccount *account = purple_buddy_get_account(buddy); - tthread::lock_guard<tthread::mutex> lock(_accountMutex); - if (_accountInstances.find(account) == _accountInstances.end()) { - LOG(ERROR) << "Callback for unknown account called"; - return; - } - IMInvoker* inst = _accountInstances[account]; - - std::string buddyName = purple_buddy_get_name(buddy); - inst->_dataModelVars.compound["buddies"].compound[buddyName] = buddyToData(buddy); + GET_INSTANCE_IN_CALLBACK(account); } -void IMInvoker::signedOnCB(PurpleConnection *gc, gpointer null) { - PurpleSavedStatus* status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); - purple_savedstatus_activate(status); +Data IMInvoker::statusToData(PurpleStatus *status) { + Data data; + const char* statusName = purple_status_get_name(status); + if (statusName) data.compound["name"] = Data(statusName, Data::VERBATIM); + + PurpleStatusType* statusType = purple_status_get_type(status); + + GList *statusAttrElem; + GList *statusAttrList = purple_status_type_get_attrs(statusType); + PurpleStatusAttr* statusAttr; + for(statusAttrElem = statusAttrList; statusAttrElem; statusAttrElem = statusAttrElem->next) { + statusAttr = (PurpleStatusAttr*)statusAttrElem->data; + const char* statusAttrId = purple_status_attr_get_id(statusAttr); + PurpleValue* statusValue = purple_status_get_attr_value(status, statusAttrId); + if (statusValue) { + data.compound[statusAttrId] = purpleValueToData(statusValue); + } + } + + data.compound["active"] = Data((bool)purple_status_is_active(status)); + data.compound["available"] = Data((bool)purple_status_is_available(status)); + data.compound["exclusive"] = Data((bool)purple_status_is_exclusive(status)); + data.compound["active"] = Data((bool)purple_status_is_active(status)); + data.compound["independent"] = Data((bool)purple_status_is_independent(status)); + data.compound["online"] = Data((bool)purple_status_is_online(status)); + + return data; } - Data IMInvoker::buddyToData(PurpleBuddy *buddy) { Data data; std::string buddyName = purple_buddy_get_name(buddy); @@ -346,53 +471,16 @@ Data IMInvoker::buddyToData(PurpleBuddy *buddy) { for(statusElem = statusList; statusElem; statusElem = statusElem->next) { status = (PurpleStatus*)statusElem->data; const char* statusId = purple_status_get_id(status); - const char* statusName = purple_status_get_name(status); PurpleStatusPrimitive statusPrimitive = purple_primitive_get_type_from_id(statusId); - + // only include active states if(statusPrimitive == PURPLE_STATUS_UNSET || !purple_presence_is_status_primitive_active(presence, statusPrimitive)) continue; - - if (statusName) data.compound["status"].compound[statusId] = Data(statusName, Data::VERBATIM); - - PurpleStatusType* statusType = purple_status_get_type(status); - - GList *statusAttrElem; - GList *statusAttrList = purple_status_type_get_attrs(statusType); - PurpleStatusAttr* statusAttr; - for(statusAttrElem = statusAttrList; statusAttrElem; statusAttrElem = statusAttrElem->next) { - statusAttr = (PurpleStatusAttr*)statusAttrElem->data; - const char* statusAttrId = purple_status_attr_get_id(statusAttr); - const char* statusAttrName = purple_status_attr_get_name(statusAttr); - - PurpleValue* statusAttrValue = purple_status_attr_get_value(statusAttr); - if (statusAttrValue) { - Data purpleValue = purpleValueToData(statusAttrValue); - if (purpleValue) { - data.compound["status"].compound[statusId].compound[statusAttrId].compound["value"] = purpleValue; - if (statusAttrName) { - data.compound["status"].compound[statusId].compound[statusAttrId].compound["name"] = Data(statusAttrName, Data::VERBATIM); - } - } - } - } - - data.compound["status"].compound[statusId].compound["active"] = Data((bool)purple_status_is_active(status)); - data.compound["status"].compound[statusId].compound["available"] = Data((bool)purple_status_is_available(status)); - data.compound["status"].compound[statusId].compound["exclusive"] = Data((bool)purple_status_is_exclusive(status)); - data.compound["status"].compound[statusId].compound["active"] = Data((bool)purple_status_is_active(status)); - data.compound["status"].compound[statusId].compound["independent"] = Data((bool)purple_status_is_independent(status)); - data.compound["status"].compound[statusId].compound["online"] = Data((bool)purple_status_is_online(status)); - - - PurpleValue* statusValue = purple_status_get_attr_value(status, statusId); - if (statusValue) - data.compound["status"].compound[statusId].compound["value"] = purpleValueToData(statusValue); - + data.compound["status"].compound[statusId] = statusToData(status); } } - std::cout << Data::toJSON(data); + return data; } @@ -407,7 +495,6 @@ Data IMInvoker::purpleValueToData(PurpleValue* value) { case PURPLE_TYPE_STRING: if (purple_value_get_string(value)) { data = Data(purple_value_get_string(value), Data::VERBATIM); - std::cout << purple_value_get_string(value) << std::endl; } break; case PURPLE_TYPE_CHAR: @@ -480,7 +567,7 @@ boost::shared_ptr<InvokerImpl> IMInvoker::create(InterpreterImpl* interpreter) { } Data IMInvoker::getDataModelVariables() { - tthread::lock_guard<tthread::mutex> lock(_accountMutex); + tthread::lock_guard<tthread::recursive_mutex> lock(_accountMutex); return _dataModelVars; } @@ -497,7 +584,7 @@ void IMInvoker::send(const SendRequest& req) { void IMInvoker::send(void *userdata, const std::string event) { // we are in the thread that manages all of libpurple EventContext* ctx = (EventContext*)userdata; -#if 0 + if (boost::iequals(ctx->sendReq.name, "im.send")) { if (ctx->instance->_account) { std::string receiver; @@ -510,7 +597,6 @@ void IMInvoker::send(void *userdata, const std::string event) { PurpleConversation* conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, ctx->instance->_account, receiver.c_str()); purple_conv_im_send(purple_conversation_get_im_data(conv), ctx->sendReq.content.c_str()); -#if 0 if (data.binary) { PurpleConnection *gc = purple_account_get_connection(ctx->instance->_account); PurplePlugin *prpl; @@ -521,18 +607,30 @@ void IMInvoker::send(void *userdata, const std::string event) { prpl = purple_connection_get_prpl(gc); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); -// if (prpl_info->send_file && -// (prpl_info->can_receive_file -// && prpl_info->can_receive_file(gc, receiver.c_str()))) -// prpl_info->send_file(gc, receiver.c_str(), file); - prpl_info->send_raw(gc, data.binary->_data, data.binary->_size); +// if (prpl_info && prpl_info->new_xfer) { +// PurpleXfer* xfer = (prpl_info->new_xfer)(purple_account_get_connection(ctx->instance->_account), receiver.c_str()); +// purple_xfer_set_local_filename(xfer, "/Users/sradomski/Documents/W3C Standards.pdf"); +// purple_xfer_set_filename(xfer, "asdfadsf.pdf"); +// purple_xfer_request(xfer); +// purple_xfer_request_accepted(xfer, "/Users/sradomski/Documents/W3C Standards.pdf"); +// } + + //Set the filename +// purple_xfer_set_local_filename(xfer, [[fileTransfer localFilename] UTF8String]); +// purple_xfer_set_filename(xfer, [[[fileTransfer localFilename] lastPathComponent] UTF8String]); +// xfer->ui_data +// purple_xfer_request(xfer); + + serv_send_file(gc, "sradomski@localhost", "/Users/sradomski/Documents/W3C Standards.pdf"); +// if (prpl_info->send_file && (prpl_info->can_receive_file && prpl_info->can_receive_file(gc, receiver.c_str()))) { +// prpl_info->send_file(gc, receiver.c_str(), "/Users/sradomski/Documents/W3C Standards.pdf"); +// } +// prpl_info->send_raw(gc, data.binary->data, data.binary->size); } } -#endif } } -#endif delete(ctx); } @@ -565,23 +663,8 @@ void IMInvoker::invoke(void *userdata, const std::string event) { _accountInstances[instance->_account] = instance; purple_account_set_password(instance->_account, password.c_str(), NULL, NULL); - purple_account_set_enabled(instance->_account, "uscxml", true); - int handle; - purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, PURPLE_CALLBACK(signedOnCB), NULL); - - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", &handle, PURPLE_CALLBACK(buddySignOnOffCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-signed-off", &handle, PURPLE_CALLBACK(buddySignOnOffCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-status-changed", &handle, PURPLE_CALLBACK(buddyStatusChangedCB), NULL); - - purple_signal_connect(purple_blist_get_handle(), "buddy-idle-changed", &handle, PURPLE_CALLBACK(buddyIdleChangeCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "update-idle", &handle, PURPLE_CALLBACK(buddyUpdateIdleCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-added", &handle, PURPLE_CALLBACK(buddyAddedCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-removed", &handle, PURPLE_CALLBACK(buddyRemovedCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "buddy-caps-changed", &handle, PURPLE_CALLBACK(buddyCapsChangedCB), NULL); - purple_signal_connect(purple_blist_get_handle(), "jabber-receiving-presence", &handle, PURPLE_CALLBACK(jabberRcvdPresenceCB), NULL); - delete(ctx); _accountMutex.unlock(); } @@ -616,24 +699,65 @@ guint IMInvoker::purpleEventInputAdd(int fd, PurpleInputCondition cond, PurpleIn short opMask = 0; if (cond & PURPLE_INPUT_READ) - opMask |= DelayedEventQueue::FD_READ; + opMask |= DelayedEventQueue::DEQ_READ; if (cond & PURPLE_INPUT_WRITE) - opMask |= DelayedEventQueue::FD_WRITE; + opMask |= DelayedEventQueue::DEQ_WRITE; guint eventId = g_rand_int(_gRand); +// std::cout << "-- Input add " << eventId << " --------" << fd << std::endl; _eventQueue->addEvent(toStr(eventId), fd, opMask, purpleCallback, ctx, true); return eventId; } gboolean IMInvoker::purpleEventInputRemove(guint handle) { +// std::cout << "-- Input del " << handle << std::endl; _eventQueue->cancelEvent(toStr(handle)); return true; } int IMInvoker::purpleEventInputGetError(int fd, int *error) { - // unused - std::cout << "purpleEventInputGetError" << std::endl; - return 0; + int ret; + socklen_t len; + len = sizeof(*error); + + ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, error, &len); + if (!ret && !(*error)) { + /* + * Taken from Fire's FaimP2PConnection.m: + * The job of this function is to detect if the connection failed or not + * There has to be a better way to do this + * + * Any socket that fails to connect will select for reading and writing + * and all reads and writes will fail + * Any listening socket will select for reading, and any read will fail + * So, select for writing, if you can write, and the write fails, not connected + */ + + { + fd_set thisfd; + struct timeval timeout; + + FD_ZERO(&thisfd); + FD_SET(fd, &thisfd); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + select(fd+1, NULL, &thisfd, NULL, &timeout); + if(FD_ISSET(fd, &thisfd)){ + ssize_t length = 0; + char buffer[4] = {0, 0, 0, 0}; + + length = write(fd, buffer, length); + if(length == -1) + { + /* Not connected */ + ret = -1; + *error = ENOTCONN; + } + } + } + } + + return ret; } void IMInvoker::purpleCallback(void *userdata, const std::string event) { @@ -642,16 +766,20 @@ void IMInvoker::purpleCallback(void *userdata, const std::string event) { ctx->function(ctx->data); delete ctx; } else if(ctx->input) { +// std::cout << "operating on " << ctx->inputFD << std::endl; ctx->input(ctx->data, ctx->inputFD, ctx->cond); } } -void IMInvoker::purplePrefsInit(void) {} +void IMInvoker::purplePrefsInit(void) { + purple_prefs_add_bool("/auto-login", false); +} + void IMInvoker::purpleDebugInit(void) {} void IMInvoker::purpleUIInit(void) { purple_accounts_set_ui_ops(&_uiAccountOps); - purple_xfers_set_ui_ops(&_uiXferOps); +// purple_xfers_set_ui_ops(&_uiXferOps); // purple_blist_set_ui_ops(&_uiBuddyOps); // purple_notify_set_ui_ops(&_uiNotifyOps); // purple_privacy_set_ui_ops(&_uiPrivacyOps); @@ -659,6 +787,10 @@ void IMInvoker::purpleUIInit(void) { // purple_connections_set_ui_ops(&_uiConnectOps); // purple_whiteboard_set_ui_ops(&_uiWhiteboardOps); purple_conversations_set_ui_ops(&_uiConvOps); + purple_debug_set_ui_ops(&_uiDebugOps); + + setupPurpleSignals(); + } void IMInvoker::purpleQuit(void) {} @@ -697,7 +829,7 @@ void* IMInvoker::accountRequestAuthorize(PurpleAccount *account, PurpleAccountRequestAuthorizationCb authorize_cb, PurpleAccountRequestAuthorizationCb deny_cb, void *user_data) { - // always accept all requests + // always accept all "may I add you as a buddy?" requests authorize_cb(message, user_data); return user_data; } @@ -788,9 +920,11 @@ void IMInvoker::purpleCancelRemote(PurpleXfer *xfer) { } gssize IMInvoker::purpleWrite(PurpleXfer *xfer, const guchar *buffer, gssize size) { std::cout << "purpleWrite" << std::endl; + return 0; } gssize IMInvoker::purpleRead(PurpleXfer *xfer, guchar **buffer, gssize size) { std::cout << "purpleRead" << std::endl; + return 0; } void IMInvoker::purpleDataNotSent(PurpleXfer *xfer, const guchar *buffer, gsize size) { std::cout << "purpleDataNotSent" << std::endl; @@ -837,34 +971,66 @@ void* IMInvoker::purpleRequestInput(const char *title, const char *primary, gboolean multiline, gboolean masked, gchar *hint, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} void* IMInvoker::purpleRequestChoice(const char *title, const char *primary, const char *secondary, gpointer default_value, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, PurpleRequestCommonParameters *cpar, - void *user_data, va_list choices) {} + void *user_data, va_list choices) { + return NULL; +} void* IMInvoker::purpleRequestAction(const char *title, const char *primary, const char *secondary, int default_action, PurpleRequestCommonParameters *cpar, void *user_data, - size_t action_count, va_list actions) {} + size_t action_count, va_list actions) { + return NULL; +} void* IMInvoker::purpleRequestWait(const char *title, const char *primary, const char *secondary, gboolean with_progress, PurpleRequestCancelCb cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} + +void IMInvoker::purpleRequestWaitUpdate(void *ui_handle, gboolean pulse, gfloat fraction) { -void IMInvoker::purpleRequestWaitUpdate(void *ui_handle, gboolean pulse, gfloat fraction) {} +} void* IMInvoker::purpleRequestFields(const char *title, const char *primary, const char *secondary, PurpleRequestFields *fields, const char *ok_text, GCallback ok_cb, const char *cancel_text, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} void* IMInvoker::purpleRequestFile(const char *title, const char *filename, gboolean savedialog, GCallback ok_cb, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + // click ok + PurpleXfer *xfer = (PurpleXfer *)user_data; + PurpleXferType xferType = purple_xfer_get_type(xfer); + if (xferType == PURPLE_XFER_RECEIVE) { + ((PurpleRequestFileCb)ok_cb)(user_data, filename); + } else if (xferType == PURPLE_XFER_SEND) { + if (xfer->local_filename != NULL && xfer->filename != NULL) { + ((PurpleRequestFileCb)ok_cb)(user_data, xfer->local_filename); + } else { + ((PurpleRequestFileCb)cancel_cb)(user_data, xfer->local_filename); + } + } + return NULL; +} + void* IMInvoker::purpleRequestFolder(const char *title, const char *dirname, GCallback ok_cb, GCallback cancel_cb, - PurpleRequestCommonParameters *cpar, void *user_data) {} -void IMInvoker::purpleRequestClose(PurpleRequestType type, void *ui_handle) {} + PurpleRequestCommonParameters *cpar, void *user_data) { + return NULL; +} + +void IMInvoker::purpleRequestClose(PurpleRequestType type, void *ui_handle) { + +} // connection ui operations @@ -885,4 +1051,14 @@ void IMInvoker::purpleDrawPont(PurpleWhiteboard *wb, int x, int y, int color, in void IMInvoker::purpleDrawLine(PurpleWhiteboard *wb, int x1, int y1, int x2, int y2, int color, int size) {} void IMInvoker::purpleClearWB(PurpleWhiteboard *wb) {} +// debug ui operations +void IMInvoker::purpleDebugPrint(PurpleDebugLevel level, const char *category, const char *arg_s) { +// std::cout << category << ": " << arg_s << std::endl; +} + +gboolean IMInvoker::purpleDebugIsEnabled(PurpleDebugLevel level, const char *category) { + return true; +} + + }
\ No newline at end of file diff --git a/src/uscxml/plugins/invoker/im/IMInvoker.h b/src/uscxml/plugins/invoker/im/IMInvoker.h index b8a57f6..51af8f8 100644 --- a/src/uscxml/plugins/invoker/im/IMInvoker.h +++ b/src/uscxml/plugins/invoker/im/IMInvoker.h @@ -13,6 +13,20 @@ extern "C" { namespace uscxml { +typedef enum { + PURPLE_BUDDY_NONE = 0x00, /**< No events. */ + PURPLE_BUDDY_SIGNON = 0x01, /**< The buddy signed on. */ + PURPLE_BUDDY_SIGNOFF = 0x02, /**< The buddy signed off. */ + PURPLE_BUDDY_INFO_UPDATED = 0x10, /**< The buddy's information (profile) changed. */ + PURPLE_BUDDY_ICON = 0x40, /**< The buddy's icon changed. */ + PURPLE_BUDDY_MISCELLANEOUS = 0x80, /**< The buddy's service-specific miscalleneous info changed. */ + PURPLE_BUDDY_SIGNON_TIME = 0x11, /**< The buddy's signon time changed. */ + PURPLE_BUDDY_EVIL = 0x12, /**< The buddy's warning level changed. */ + PURPLE_BUDDY_DIRECTIM_CONNECTED = 0x14, /**< Connected to the buddy via DirectIM. */ + PURPLE_BUDDY_DIRECTIM_DISCONNECTED = 0x18, /**< Disconnected from the buddy via DirectIM. */ + PURPLE_BUDDY_NAME = 0x20 /**<Buddy name (UID) changed. */ +} PurpleBuddyEvent; + class IMInvoker : public InvokerImpl { public: struct EventContext { @@ -38,13 +52,14 @@ public: virtual void cancel(const std::string sendId); virtual void invoke(const InvokeRequest& req); -protected: +private: static bool _libPurpleIsInitialized; static Data _pluginData; Data _dataModelVars; static Data buddyToData(PurpleBuddy *buddy); + static Data statusToData(PurpleStatus *status); static Data purpleValueToData(PurpleValue* value); static PurpleAccountUiOps _uiAccountOps; @@ -58,31 +73,39 @@ protected: static PurpleRequestUiOps _uiRequestOps; static PurpleConnectionUiOps _uiConnectOps; static PurpleWhiteboardUiOps _uiWhiteboardOps; + static PurpleDebugUiOps _uiDebugOps; static PurpleRequestFeature _features; static GHashTable* _uiInfo; static GRand* _gRand; - static tthread::mutex _accountMutex; + static tthread::recursive_mutex _accountMutex; static std::map<PurpleAccount*, IMInvoker*> _accountInstances; static tthread::mutex _initMutex; static tthread::condition_variable _initCond; static DelayedEventQueue* _eventQueue; - // event callbacks + // libpurple event callbacks static void signedOnCB(PurpleConnection *gc, gpointer null); - static void buddySignOnOffCB(PurpleBuddy *buddy); - static void buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *oldstatus, PurpleStatus *newstatus); - static void buddyIdleChangeCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle); - static void buddyUpdateIdleCB(); + static void conversationCreatedCB(PurpleConversation *conv, void *data); + static void chatJoinedCB(PurpleConversation *conv, void *data); + static void chatJoinFailedCB(PurpleConnection *gc, GHashTable *components); + static void buddyTypingCB(PurpleAccount *account, const char *name, void *data); + static void buddyTypedCB(PurpleAccount *account, const char *name, void *data); + static void buddyTypingStoppedCB(PurpleAccount *account, const char *name, void *data); + static void buddyIdleChangedCB(PurpleBuddy *buddy, gboolean old_idle, gboolean idle, PurpleBuddyEvent event); + static void blistNodeAliasedCB(PurpleBlistNode *node, char *old_alias); + static void buddyEventCB(PurpleBuddy *buddy, PurpleBuddyEvent event); + static void buddyStatusChangedCB(PurpleBuddy *buddy, PurpleStatus *oldstatus, PurpleStatus *newstatus, PurpleBuddyEvent event); static void buddyAddedCB(PurpleBuddy* buddy); static void buddyRemovedCB(PurpleBuddy* buddy); + static void fileRecvRequestCB(PurpleXfer *xfer); static void buddyCapsChangedCB(PurpleBuddy* buddy, PurpleMediaCaps newcaps, PurpleMediaCaps oldcaps); - static gboolean jabberRcvdPresenceCB(PurpleConnection *gc, const char *type, const char *from, xmlnode *presence); - // these are only being called from the delayed queue's thread static void initLibPurple(void *userdata, const std::string event); + static void setupPurpleSignals(); + static void send(void *userdata, const std::string event); static void invoke(void *userdata, const std::string event); @@ -104,6 +127,11 @@ protected: }; static void purpleCallback(void *userdata, const std::string event); + // libpurple debug + static void purpleDebugPrint(PurpleDebugLevel level, const char *category, const char *arg_s); + static gboolean purpleDebugIsEnabled(PurpleDebugLevel level, const char *category); + + // libpurple core operations static void purplePrefsInit(void); static void purpleDebugInit(void); diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp index 8799fc2..28b3ba4 100644 --- a/src/uscxml/server/HTTPServer.cpp +++ b/src/uscxml/server/HTTPServer.cpp @@ -8,6 +8,8 @@ #include "uscxml/server/HTTPServer.h" #include "uscxml/Message.h" #include "uscxml/Factory.h" + +#include <string> #include <iostream> #include <event2/dns.h> #include <event2/event.h> @@ -17,9 +19,6 @@ #include <event2/http_struct.h> #include <event2/thread.h> -#include <string> -#include <iostream> - #include <glog/logging.h> #include <boost/algorithm/string.hpp> @@ -28,28 +27,38 @@ #include <arpa/inet.h> #endif +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) +#include <openssl/ssl.h> +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <event2/bufferevent_ssl.h> +#endif + #ifdef BUILD_AS_PLUGINS #include <Pluma/Connector.hpp> #endif namespace uscxml { -HTTPServer::HTTPServer(unsigned short port) { +HTTPServer::HTTPServer(unsigned short port, SSLConfig* sslConf) { _port = port; _base = event_base_new(); _http = evhttp_new(_base); _thread = NULL; - - evhttp_set_allowed_methods(_http, - EVHTTP_REQ_GET | - EVHTTP_REQ_POST | - EVHTTP_REQ_HEAD | - EVHTTP_REQ_PUT | - EVHTTP_REQ_DELETE | - EVHTTP_REQ_OPTIONS | - EVHTTP_REQ_TRACE | - EVHTTP_REQ_CONNECT | - EVHTTP_REQ_PATCH); // allow all methods + + unsigned int allowedMethods = + EVHTTP_REQ_GET | + EVHTTP_REQ_POST | + EVHTTP_REQ_HEAD | + EVHTTP_REQ_PUT | + EVHTTP_REQ_DELETE | + EVHTTP_REQ_OPTIONS | + EVHTTP_REQ_TRACE | + EVHTTP_REQ_CONNECT | + EVHTTP_REQ_PATCH; + + evhttp_set_allowed_methods(_http, allowedMethods); // allow all methods _handle = NULL; while((_handle = evhttp_bind_socket_with_handle(_http, INADDR_ANY, _port)) == NULL) { @@ -57,6 +66,46 @@ HTTPServer::HTTPServer(unsigned short port) { } determineAddress(); +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) + if (!sslConf) { + _https = NULL; + _sslHandle = NULL; + _sslPort = 0; + } else { + _sslPort = sslConf->port; + + // Initialize OpenSSL + SSL_library_init(); + ERR_load_crypto_strings(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + _https = evhttp_new(_base); + evhttp_set_allowed_methods(_https, allowedMethods); // allow all methods + + SSL_CTX* ctx = SSL_CTX_new (SSLv23_server_method ()); + SSL_CTX_set_options(ctx, + SSL_OP_SINGLE_DH_USE | + SSL_OP_SINGLE_ECDH_USE | + SSL_OP_NO_SSLv2); + + EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + SSL_CTX_set_tmp_ecdh (ctx, ecdh); + + SSL_CTX_use_certificate_chain_file(ctx, sslConf->publicKey.c_str()); + SSL_CTX_use_PrivateKey_file(ctx, sslConf->privateKey.c_str(), SSL_FILETYPE_PEM); + SSL_CTX_check_private_key(ctx); + + evhttp_set_bevcb(_https, sslBufferEventCallback, ctx); + evhttp_set_gencb(_https, sslGeneralBufferEventCallback, NULL); + + _sslHandle = NULL; + while((_sslHandle = evhttp_bind_socket_with_handle(_https, INADDR_ANY, _sslPort)) == NULL) { + _sslPort++; + } + } +#endif + // evhttp_set_timeout(_http, 5); // generic callback @@ -72,7 +121,7 @@ HTTPServer::~HTTPServer() { HTTPServer* HTTPServer::_instance = NULL; tthread::recursive_mutex HTTPServer::_instanceMutex; -HTTPServer* HTTPServer::getInstance(int port) { +HTTPServer* HTTPServer::getInstance(unsigned short port, SSLConfig* sslConf) { // tthread::lock_guard<tthread::recursive_mutex> lock(_instanceMutex); if (_instance == NULL) { #ifdef _WIN32 @@ -84,12 +133,91 @@ HTTPServer* HTTPServer::getInstance(int port) { #else evthread_use_windows_threads(); #endif - _instance = new HTTPServer(port); + _instance = new HTTPServer(port, sslConf); _instance->start(); } return _instance; } +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) +// see https://github.com/ppelleti/https-example/blob/master/https-server.c +struct bufferevent* HTTPServer::sslBufferEventCallback(struct event_base *base, void *arg) { + struct bufferevent* r; + SSL_CTX *ctx = (SSL_CTX *) arg; + r = bufferevent_openssl_socket_new (base, + -1, + SSL_new (ctx), + BUFFEREVENT_SSL_ACCEPTING, + BEV_OPT_CLOSE_ON_FREE); + return r; +} + + +void HTTPServer::sslGeneralBufferEventCallback (struct evhttp_request *req, void *arg) { + struct evbuffer *evb = NULL; + const char *uri = evhttp_request_get_uri (req); + struct evhttp_uri *decoded = NULL; + + /* We only handle POST requests. */ + if (evhttp_request_get_command (req) != EVHTTP_REQ_POST) + { evhttp_send_reply (req, 200, "OK", NULL); + return; + } + + printf ("Got a POST request for <%s>\n", uri); + + /* Decode the URI */ + decoded = evhttp_uri_parse (uri); + if (! decoded) + { printf ("It's not a good URI. Sending BADREQUEST\n"); + evhttp_send_error (req, HTTP_BADREQUEST, 0); + return; + } + + /* Decode the payload */ + struct evkeyvalq kv; + memset (&kv, 0, sizeof (kv)); + struct evbuffer *buf = evhttp_request_get_input_buffer (req); + evbuffer_add (buf, "", 1); /* NUL-terminate the buffer */ + char *payload = (char *) evbuffer_pullup (buf, -1); + if (0 != evhttp_parse_query_str (payload, &kv)) + { printf ("Malformed payload. Sending BADREQUEST\n"); + evhttp_send_error (req, HTTP_BADREQUEST, 0); + return; + } + + /* Determine peer */ + char *peer_addr; + ev_uint16_t peer_port; + struct evhttp_connection *con = evhttp_request_get_connection (req); + evhttp_connection_get_peer (con, &peer_addr, &peer_port); + + /* Extract passcode */ + const char *passcode = evhttp_find_header (&kv, "passcode"); + char response[256]; + evutil_snprintf (response, sizeof (response), + "Hi %s! I %s your passcode.\n", peer_addr, + (0 == strcmp (passcode, "R23") + ? "liked" + : "didn't like")); + evhttp_clear_headers (&kv); /* to free memory held by kv */ + + /* This holds the content we're sending. */ + evb = evbuffer_new (); + + evhttp_add_header (evhttp_request_get_output_headers (req), + "Content-Type", "application/x-yaml"); + evbuffer_add (evb, response, strlen (response)); + + evhttp_send_reply (req, 200, "OK", evb); + + if (decoded) + evhttp_uri_free (decoded); + if (evb) + evbuffer_free (evb); +} +#endif + /** * This callback is registered for all HTTP requests */ diff --git a/src/uscxml/server/HTTPServer.h b/src/uscxml/server/HTTPServer.h index 3e0d91c..141ce7e 100644 --- a/src/uscxml/server/HTTPServer.h +++ b/src/uscxml/server/HTTPServer.h @@ -26,6 +26,14 @@ public: } }; + class SSLConfig { + public: + SSLConfig() : port(8443) {} + std::string privateKey; + std::string publicKey; + unsigned short port; + }; + class Reply { public: Reply(Request req) : status(200), type(req.data.compound["type"].atom), curlReq(req.curlReq) {} @@ -41,7 +49,7 @@ public: evhttp_request* httpReq; }; - static HTTPServer* getInstance(int port = 8080); + static HTTPServer* getInstance(unsigned short port = 8080, SSLConfig* sslConf = NULL); static std::string getBaseURL(); static void reply(const Reply& reply); @@ -58,7 +66,7 @@ private: }; }; - HTTPServer(unsigned short port); + HTTPServer(unsigned short port, SSLConfig* sslConf = NULL); ~HTTPServer(); void start(); @@ -90,6 +98,15 @@ private: bool _isRunning; friend class HTTPServlet; + +#if (defined EVENT_SSL_FOUND && defined OPENSSL_FOUND) + struct evhttp* _https; + struct evhttp_bound_socket* _sslHandle; + unsigned short _sslPort; + + static struct bufferevent* sslBufferEventCallback(struct event_base *base, void *arg); + static void sslGeneralBufferEventCallback (struct evhttp_request *req, void *arg); +#endif }; class HTTPServlet { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index de7fa70..51fe962 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -68,6 +68,11 @@ if (NOT WIN32) add_test(test-stress ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-stress ${CMAKE_SOURCE_DIR}/test/samples/w3c) set_target_properties(test-stress PROPERTIES FOLDER "Tests") + add_executable(test-instant-messaging src/test-instant-messaging.cpp) + target_link_libraries(test-instant-messaging uscxml) + add_test(test-instant-messaging ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-instant-messaging) + set_target_properties(test-instant-messaging PROPERTIES FOLDER "Tests") + endif() add_executable(test-url src/test-url.cpp) @@ -75,6 +80,12 @@ target_link_libraries(test-url uscxml) add_test(test-url ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-url) set_target_properties(test-url PROPERTIES FOLDER "Tests") + +add_executable(test-cmdline-parsing src/test-cmdline-parsing.cpp) +target_link_libraries(test-cmdline-parsing uscxml) +add_test(test-cmdline-parsing ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-cmdline-parsing) +set_target_properties(test-cmdline-parsing PROPERTIES FOLDER "Tests") + # add_executable(test-initial-config src/test-initial-config.cpp) # target_link_libraries(test-initial-config uscxml) # add_test(test-url ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test-initial-config ${CMAKE_SOURCE_DIR}/test/samples/uscxml/test-initial-config.scxml) diff --git a/test/samples/uscxml/test-instant-messaging.scxml b/test/samples/uscxml/test-instant-messaging.scxml index 6ca39d4..588b2b7 100644 --- a/test/samples/uscxml/test-instant-messaging.scxml +++ b/test/samples/uscxml/test-instant-messaging.scxml @@ -19,18 +19,25 @@ <param name="username" expr="'uscxml@localhost'" /> <param name="password" expr="'password'" /> <param name="protocol" expr="'prpl-jabber'" /> + <finalize> + <script>print("-----------------\n"); dump(_event);</script> + </finalize> </invoke> <state id="dump"> <transition event="dump" target="dump"> <send event="dump" delay="1000ms" /> <script>//dump(_invokers['im']);</script> - <send target="#_im" event="im.send"> - <param name="receiver" expr="'sradomski@localhost'" /> - <param name="data" expr="someBinaryData" /> - <content>Hey There!</content> - </send> </transition> </state> + + <transition event="im.buddy.status.changed" cond="_event.data.name === 'sradomski@localhost'"> + <send target="#_im" event="im.send"> + <param name="receiver" expr="'sradomski@localhost'" /> + <param name="data" expr="someBinaryData" /> + <content>Hey There!</content> + </send> + </transition> + </state> </scxml>
\ No newline at end of file diff --git a/test/samples/uscxml/test-performance.scxml b/test/samples/uscxml/test-performance.scxml index a102a18..4d2d4c4 100644 --- a/test/samples/uscxml/test-performance.scxml +++ b/test/samples/uscxml/test-performance.scxml @@ -1,6 +1,6 @@ <scxml datamodel="ecmascript"> <datamodel> - <data id="iterations">1000</data> + <data id="iterations">10000</data> </datamodel> <state id="start"> <transition target="loop" /> diff --git a/test/samples/uscxml/test-startup-time.scxml b/test/samples/uscxml/test-startup-time.scxml new file mode 100644 index 0000000..cc10f04 --- /dev/null +++ b/test/samples/uscxml/test-startup-time.scxml @@ -0,0 +1,4 @@ +<scxml datamodel="ecmascript"> + <script>print("asdf");</script> + <state id="foo" final="true" /> +</scxml>
\ No newline at end of file diff --git a/test/src/test-arabica-events.cpp b/test/src/test-arabica-events.cpp index 193b7ae..0cd45ee 100644 --- a/test/src/test-arabica-events.cpp +++ b/test/src/test-arabica-events.cpp @@ -1,3 +1,5 @@ +#include <iostream> + #include "uscxml/config.h" #include "uscxml/Common.h" #include <DOM/Document.hpp> diff --git a/test/src/test-arabica-parsing.cpp b/test/src/test-arabica-parsing.cpp index 24c58ae..24275fc 100644 --- a/test/src/test-arabica-parsing.cpp +++ b/test/src/test-arabica-parsing.cpp @@ -1,3 +1,5 @@ +#include <iostream> + #include "uscxml/config.h" #include "uscxml/Common.h" #include <DOM/Document.hpp> diff --git a/test/src/test-arabica-xpath.cpp b/test/src/test-arabica-xpath.cpp index 408cc2b..fe457f4 100644 --- a/test/src/test-arabica-xpath.cpp +++ b/test/src/test-arabica-xpath.cpp @@ -1,3 +1,5 @@ +#include <iostream> + #include <XPath/XPath.hpp> #include <DOM/Simple/DOMImplementation.hpp> #include <DOM/io/Stream.hpp> diff --git a/test/src/test-cmdline-parsing.cpp b/test/src/test-cmdline-parsing.cpp new file mode 100644 index 0000000..b0b909e --- /dev/null +++ b/test/src/test-cmdline-parsing.cpp @@ -0,0 +1,109 @@ +#include "uscxml/config.h" +#include "uscxml/Interpreter.h" +#include <glog/logging.h> + +int main(int argc, char** argv) { + using namespace uscxml; + + if (true) { + int testArgc = 11; + const char* testArgv[] = { + "test-cmdline-parsing", + "--verbose", + "--dot", + "--port=80", + "--ssl-port=8080", + "--certificate=/foo/bar.pem", + "--private-key=/foo/bar.priv", + "--public-key=/foo/bar.pub", + "--plugin-path=/foo/plugins", + "--loglevel=10", + "--disable-http", + 0 + }; + InterpreterOptions options = InterpreterOptions::fromCmdLine(testArgc, (char **)testArgv); + assert(options.verbose); + assert(options.useDot); + assert(options.httpPort == 80); + assert(options.httpsPort == 8080); + assert(boost::equals(options.certificate, "/foo/bar.pem")); + assert(boost::equals(options.privateKey, "/foo/bar.priv")); + assert(boost::equals(options.publicKey, "/foo/bar.pub")); + assert(boost::equals(options.pluginPath, "/foo/plugins")); + assert(options.logLevel == 10); + assert(!options.withHTTP); + assert(!options); // invalid as no SCXML document is given + + optind = 0; + optreset = 1; + } + + if (true) { + int testArgc = 3; + const char* testArgv[] = { + "test-cmdline-parsing", + "--verbose", + "/foo/bar.scxml", + 0 + }; + InterpreterOptions options = InterpreterOptions::fromCmdLine(testArgc, (char **)testArgv); + assert(options); + assert(options.verbose); + assert(options.interpreters.size() == 1); + assert(options.interpreters.find("/foo/bar.scxml") != options.interpreters.end()); + + optind = 0; + optreset = 1; + } + + if (true) { + int testArgc = 7; + const char* testArgv[] = { + "test-cmdline-parsing", + "--port=80", + "/foo/bar1.scxml", + "--disable-http", + "/foo/bar2.scxml", + "/foo/bar3.scxml", + "--disable-http", + 0 + }; + InterpreterOptions options = InterpreterOptions::fromCmdLine(testArgc, (char **)testArgv); + assert(options); + assert(options.httpPort == 80); + assert(options.interpreters.size() == 3); + assert(options.interpreters.find("/foo/bar1.scxml") != options.interpreters.end()); + assert(options.interpreters.find("/foo/bar2.scxml") != options.interpreters.end()); + assert(options.interpreters.find("/foo/bar3.scxml") != options.interpreters.end()); + assert(!options.interpreters["/foo/bar1.scxml"]->withHTTP); + assert(options.interpreters["/foo/bar2.scxml"]->withHTTP); + assert(!options.interpreters["/foo/bar3.scxml"]->withHTTP); + + optind = 0; + optreset = 1; + } + + if (true) { + int testArgc = 5; + const char* testArgv[] = { + "test-cmdline-parsing", + "--port=80", + "/foo/bar1.scxml", + "--vrml-path=/foo/bar.test", + "--tmp-path=/foo/bar.test", + 0 + }; + InterpreterOptions options = InterpreterOptions::fromCmdLine(testArgc, (char **)testArgv); + assert(options); + assert(options.httpPort == 80); + assert(options.interpreters.size() == 1); + assert(options.interpreters.find("/foo/bar1.scxml") != options.interpreters.end()); + assert(options.interpreters["/foo/bar1.scxml"]->additionalParameters.find("vrml-path") != options.interpreters["/foo/bar1.scxml"]->additionalParameters.end()); + assert(options.interpreters["/foo/bar1.scxml"]->additionalParameters.find("tmp-path") != options.interpreters["/foo/bar1.scxml"]->additionalParameters.end()); + + optind = 0; + optreset = 1; + } + + return EXIT_SUCCESS; +}
\ No newline at end of file diff --git a/test/src/test-instant-messaging.cpp b/test/src/test-instant-messaging.cpp new file mode 100644 index 0000000..0bf2898 --- /dev/null +++ b/test/src/test-instant-messaging.cpp @@ -0,0 +1,298 @@ +/* + * pidgin + * + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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 02111-1301 USA + * + */ + +#include "purple.h" + +#include <glib.h> + +#include <signal.h> +#include <string.h> +#ifndef _WIN32 +#include <unistd.h> +#else +#include "win32/win32dep.h" +#endif + +#define CUSTOM_USER_DIRECTORY "/dev/null" +#define CUSTOM_PLUGIN_PATH "" +#define PLUGIN_SAVE_PREF "/purple/nullclient/plugins/saved" +#define UI_ID "nullclient" + +/** + * The following eventloop functions are used in both pidgin and purple-text. If your + * application uses glib mainloop, you can safely use this verbatim. + */ +#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +typedef struct _PurpleGLibIOClosure { + PurpleInputFunction function; + guint result; + gpointer data; +} PurpleGLibIOClosure; + +static void purple_glib_io_destroy(gpointer data) +{ + g_free(data); +} + +static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + PurpleGLibIOClosure *closure = (PurpleGLibIOClosure*)data; + int purple_cond = 0; + + if (condition & PURPLE_GLIB_READ_COND) + purple_cond |= PURPLE_INPUT_READ; + if (condition & PURPLE_GLIB_WRITE_COND) + purple_cond |= PURPLE_INPUT_WRITE; + + closure->function(closure->data, g_io_channel_unix_get_fd(source), + (PurpleInputCondition)purple_cond); + + return TRUE; +} + +static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function, + gpointer data) +{ + PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1); + GIOChannel *channel; + int cond = 0; + + closure->function = function; + closure->data = data; + + if (condition & PURPLE_INPUT_READ) + cond |= PURPLE_GLIB_READ_COND; + if (condition & PURPLE_INPUT_WRITE) + cond |= PURPLE_GLIB_WRITE_COND; + +#if defined _WIN32 && !defined WINPIDGIN_USE_GLIB_IO_CHANNEL + channel = wpurple_g_io_channel_win32_new_socket(fd); +#else + channel = g_io_channel_unix_new(fd); +#endif + closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, (GIOCondition)cond, + purple_glib_io_invoke, closure, purple_glib_io_destroy); + + g_io_channel_unref(channel); + return closure->result; +} + +static PurpleEventLoopUiOps glib_eventloops = +{ + g_timeout_add, + g_source_remove, + glib_input_add, + g_source_remove, + NULL, + g_timeout_add_seconds, + + /* padding */ + NULL, + NULL, + NULL +}; +/*** End of the eventloop functions. ***/ + +/*** Conversation uiops ***/ +static void +null_write_conv(PurpleConversation *conv, const char *who, const char *alias, + const char *message, PurpleMessageFlags flags, time_t mtime) +{ + const char *name; + if (alias && *alias) + name = alias; + else if (who && *who) + name = who; + else + name = NULL; + + printf("(%s) %s %s: %s\n", purple_conversation_get_name(conv), + purple_utf8_strftime("(%H:%M:%S)", localtime(&mtime)), + name, message); +} + +static PurpleConversationUiOps null_conv_uiops = +{ + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + null_write_conv, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL +}; + +static void +null_ui_init(void) +{ + /** + * This should initialize the UI components for all the modules. Here we + * just initialize the UI for conversations. + */ + purple_conversations_set_ui_ops(&null_conv_uiops); +} + +static PurpleCoreUiOps null_core_uiops = +{ + NULL, + NULL, + null_ui_init, + NULL, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void +init_libpurple(void) +{ + /* Set a custom user directory (optional) */ + purple_util_set_user_dir(CUSTOM_USER_DIRECTORY); + + /* We do not want any debugging for now to keep the noise to a minimum. */ + purple_debug_set_enabled(FALSE); + + /* Set the core-uiops, which is used to + * - initialize the ui specific preferences. + * - initialize the debug ui. + * - initialize the ui components for all the modules. + * - uninitialize the ui components for all the modules when the core terminates. + */ + purple_core_set_ui_ops(&null_core_uiops); + + /* Set the uiops for the eventloop. If your client is glib-based, you can safely + * copy this verbatim. */ + purple_eventloop_set_ui_ops(&glib_eventloops); + + /* Set path to search for plugins. The core (libpurple) takes care of loading the + * core-plugins, which includes the protocol-plugins. So it is not essential to add + * any path here, but it might be desired, especially for ui-specific plugins. */ + purple_plugins_add_search_path(CUSTOM_PLUGIN_PATH); + + /* Now that all the essential stuff has been set, let's try to init the core. It's + * necessary to provide a non-NULL name for the current ui to the core. This name + * is used by stuff that depends on this ui, for example the ui-specific plugins. */ + if (!purple_core_init(UI_ID)) { + /* Initializing the core failed. Terminate. */ + fprintf(stderr, + "libpurple initialization failed. Dumping core.\n" + "Please report this!\n"); + abort(); + } + + /* Load the preferences. */ + purple_prefs_load(); + + /* Load the desired plugins. The client should save the list of loaded plugins in + * the preferences using purple_plugins_save_loaded(PLUGIN_SAVE_PREF) */ + purple_plugins_load_saved(PLUGIN_SAVE_PREF); +} + +static void +signed_on(PurpleConnection *gc, gpointer null) +{ + PurpleAccount *account = purple_connection_get_account(gc); + printf("Account connected: %s %s\n", purple_account_get_username(account), purple_account_get_protocol_id(account)); +} + +static void +buddy_signed_on(PurpleBuddy *buddy) { + PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy)); + PurplePlugin* prpl = purple_connection_get_prpl(gc); + PurplePluginProtocolInfo* prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); + + if (prpl_info->send_file && (prpl_info->can_receive_file && prpl_info->can_receive_file(gc, "sradomski@localhost"))) { + prpl_info->send_file(gc, "sradomski@localhost", "/Users/sradomski/Documents/W3C Standards.pdf"); + } + +} + +static void +connect_to_signals_for_demonstration_purposes_only(void) +{ + static int handle; + purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle, + PURPLE_CALLBACK(signed_on), NULL); + purple_signal_connect(purple_blist_get_handle(), "buddy-signed-on", &handle, + PURPLE_CALLBACK(buddy_signed_on), NULL); +} + +int main(int argc, char *argv[]) +{ + GList *iter; + GMainLoop *loop = g_main_loop_new(NULL, FALSE); + PurpleAccount *account; + PurpleSavedStatus *status; + +#ifndef _WIN32 + /* libpurple's built-in DNS resolution forks processes to perform + * blocking lookups without blocking the main process. It does not + * handle SIGCHLD itself, so if the UI does not you quickly get an army + * of zombie subprocesses marching around. + */ + signal(SIGCHLD, SIG_IGN); +#endif + + init_libpurple(); + + printf("libpurple initialized.\n"); + + iter = purple_plugins_get_protocols(); + + /* Create the account */ + account = purple_account_new("uscxml@localhost", "prpl-jabber"); + + /* Get the password for the account */ + purple_account_set_password(account, "password", NULL, NULL); + + /* It's necessary to enable the account first. */ + purple_account_set_enabled(account, UI_ID, TRUE); + + /* Now, to connect the account(s), create a status and activate it. */ + status = purple_savedstatus_new(NULL, PURPLE_STATUS_AVAILABLE); + purple_savedstatus_activate(status); + + connect_to_signals_for_demonstration_purposes_only(); + + g_main_loop_run(loop); + + return 0; +} + |